diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2017-02-27 08:56:17 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2017-02-27 08:56:17 +0200 |
commit | fafe2d8d6eecd2272aabe65bdfff9d704c8548b7 (patch) | |
tree | 6d0351860ef82e58d0a704bdc13accf1bab35ef9 | |
parent | eb00a46f8eb47a31bb4d4c5c4af72153239103e3 (diff) | |
download | jumper-fafe2d8d6eecd2272aabe65bdfff9d704c8548b7.tar.gz jumper-fafe2d8d6eecd2272aabe65bdfff9d704c8548b7.tar.bz2 |
Improve the ifalive utility
* extra/.gitignore: New file.
* extra/Makefile.am: Add new sources.
* extra/expr.y: New file.
* extra/ifalive.c: The -c operation treats optional arguments as
an expression that returns true if the interface exhibits some
activity (is "alive"), and false otherwise.
* extra/ifalive.h: New file.
* extra/lex.l: New file.
-rw-r--r-- | extra/.gitignore | 5 | ||||
-rw-r--r-- | extra/Makefile.am | 7 | ||||
-rw-r--r-- | extra/expr.y | 415 | ||||
-rw-r--r-- | extra/ifalive.c | 55 | ||||
-rw-r--r-- | extra/ifalive.h | 28 | ||||
-rw-r--r-- | extra/lex.l | 98 |
6 files changed, 577 insertions, 31 deletions
diff --git a/extra/.gitignore b/extra/.gitignore new file mode 100644 index 0000000..2881690 --- /dev/null +++ b/extra/.gitignore @@ -0,0 +1,5 @@ +expr.c +expr.h +expr.output +ifalive +lex.c diff --git a/extra/Makefile.am b/extra/Makefile.am index 8cbcf01..9c03b60 100644 --- a/extra/Makefile.am +++ b/extra/Makefile.am @@ -1,2 +1,7 @@ libexec_PROGRAMS=ifalive -ifalive_SOURCES=ifalive.c +ifalive_SOURCES=\ + ifalive.c\ + expr.y\ + lex.l + +AM_YFLAGS = -dv diff --git a/extra/expr.y b/extra/expr.y new file mode 100644 index 0000000..098fc3e --- /dev/null +++ b/extra/expr.y @@ -0,0 +1,415 @@ +%{ +#include <config.h> +#include <stdlib.h> +#include <stdint.h> +#include <assert.h> +#include <string.h> +#include "expr.h" +#include "ifalive.h" + +enum expr_datatype { + dt_int, + dt_bool, + dt_double, + + DT_MAX +}; + +struct expr_var { + int var; + int field; +}; + +enum expr_node_type { + node_value, + node_var, + node_unary, + node_binary +}; + +enum expr_binop { + binop_add, + binop_sub, + binop_mul, + binop_div, + binop_eq, + binop_ne, + binop_gt, + binop_ge, + binop_lt, + binop_le, + binop_and, + binop_or +}; + +enum expr_unop { + unop_not, + unop_minus, + unop_typecast +}; + +struct expr_node { + enum expr_node_type type; + enum expr_datatype datatype; + union { + uintmax_t v_int; + int v_bool; + double v_double; + struct expr_var var; + struct { + enum expr_binop opcode; + struct expr_node *operand[2]; + } binary; + struct { + enum expr_unop opcode; + struct expr_node *operand; + } unary; + } v; +}; + +static struct expr_node * +expr_node_alloc(void) +{ + struct expr_node *p = malloc(sizeof(*p)); + assert(p != NULL); + return p; +} + +enum expr_datatype expr_promoted_type[DT_MAX][DT_MAX] = { + [dt_int] = { + [dt_int] = dt_int, + [dt_double] = dt_double, + [dt_bool] = dt_int + }, + [dt_double] = { + [dt_int] = dt_double, + [dt_double] = dt_double, + [dt_bool] = dt_double + }, + [dt_bool] = { + [dt_int] = dt_int, + [dt_double] = dt_double, + [dt_bool] = dt_bool + }, +}; + +struct expr_node * +expr_cast(enum expr_datatype t, struct expr_node *a) +{ + if (t != a->datatype) { + struct expr_node *res = expr_node_alloc(); + res->type = node_unary; + res->datatype = t; + res->v.unary.opcode = unop_typecast; + res->v.unary.operand = a; + a = res; + } + return a; +} + +static struct expr_node * +expr_binop(struct expr_node *a, struct expr_node *b, + enum expr_binop opcode, enum expr_datatype datatype) +{ + struct expr_node *res; + + res = expr_node_alloc(); + res->type = node_binary; + res->datatype = datatype; + res->v.binary.opcode = opcode; + res->v.binary.operand[0] = expr_cast(res->datatype, a); + res->v.binary.operand[1] = expr_cast(res->datatype, b); + return res; +} + +static struct expr_node * +expr_binop_comp(struct expr_node *a, struct expr_node *b, + enum expr_binop opcode) +{ + struct expr_node *res; + enum expr_datatype t = expr_promoted_type[a->datatype][b->datatype]; + + res = expr_node_alloc(); + res->type = node_binary; + res->datatype = dt_bool; + res->v.binary.opcode = opcode; + res->v.binary.operand[0] = expr_cast(t, a); + res->v.binary.operand[1] = expr_cast(t, b); + return res; +} + +void yyerror(char const *s); + +static struct expr_node *parse_tree; +%} + +%token <var> VAR +%token <field> FIELD +%token <value> VALUE + +%left OR +%left AND +%left EQ NE +%left LT LE GT GE +%left '+' '-' +%left '*' '/' +%left NOT UMINUS + +%type <node> term expr + +%union { + int var; + int field; + uintmax_t value; + struct expr_node *node; +} + +%% +input: expr + { + parse_tree = expr_cast(dt_bool, $1); + } + ; + +expr : term + | NOT expr + { + $$ = expr_node_alloc(); + $$->type = node_unary; + $$->datatype = dt_bool; + $$->v.unary.opcode = unop_not; + $$->v.unary.operand = expr_cast(dt_bool, $2); + } + | '-' expr %prec UMINUS + { + $$ = expr_node_alloc(); + $$->type = node_unary; + $$->datatype = dt_double; + $$->v.unary.opcode = unop_minus; + $$->v.unary.operand = expr_cast(dt_double, $2); + } + | expr AND expr + { + $$ = expr_binop($1, $3, binop_and, dt_bool); + } + | expr OR expr + { + $$ = expr_binop($1, $3, binop_or, dt_bool); + } + | expr EQ expr + { + $$ = expr_binop_comp($1, $3, binop_eq); + } + | expr NE expr + { + $$ = expr_binop_comp($1, $3, binop_ne); + } + | expr LT expr + { + $$ = expr_binop_comp($1, $3, binop_lt); + } + | expr LE expr + { + $$ = expr_binop_comp($1, $3, binop_le); + } + | expr GT expr + { + $$ = expr_binop_comp($1, $3, binop_gt); + } + | expr GE expr + { + $$ = expr_binop_comp($1, $3, binop_ge); + } + | expr '+' expr + { + $$ = expr_binop($1, $3, dt_double, binop_add); + } + | expr '-' expr + { + $$ = expr_binop($1, $3, dt_double, binop_sub); + } + | expr '*' expr + { + $$ = expr_binop($1, $3, dt_double, binop_mul); + } + | expr '/' expr + { + $$ = expr_binop($1, $3, dt_double, binop_div); + } + | '(' expr ')' + { + $$ = $2; + } + ; + +term : VAR '.' FIELD + { + $$ = expr_node_alloc(); + $$->type = node_var; + $$->datatype = dt_int; + $$->v.var.var = $1; + $$->v.var.field = $3; + } + | VALUE + { + $$ = expr_node_alloc(); + $$->type = node_value; + $$->datatype = dt_int; + $$->v.v_int = $1; + } + ; + +%% + +struct eval_env +{ + IFSTAT stat[2]; +}; + +union eval_res { + uintmax_t v_int; + int v_bool; + double v_double; +}; + +#define EVAL_COMP(op,node,a,b,res) \ + switch ((node)->v.binary.operand[0]->datatype) { \ + case dt_int: \ + res->v_bool = a.v_int op b.v_int; \ + break; \ + case dt_bool: \ + res->v_bool = a.v_bool op b.v_bool; \ + break; \ + case dt_double: \ + res->v_bool = a.v_double op b.v_double; \ + } + + +static void +node_eval(struct expr_node *node, struct eval_env *env, union eval_res *res) +{ + union eval_res a, b; + + switch (node->type) { + case node_value: + assert(node->datatype == dt_int); + res->v_int = node->v.v_int; + break; + + case node_var: + res->v_int = env->stat[node->v.var.var][node->v.var.field]; + break; + + case node_binary: + node_eval(node->v.binary.operand[0], env, &a); + node_eval(node->v.binary.operand[1], env, &b); + + switch (node->v.binary.opcode) { + case binop_add: + assert(node->datatype == dt_double); + res->v_double = a.v_double + b.v_double; + break; + + case binop_sub: + assert(node->datatype == dt_double); + res->v_double = a.v_double - b.v_double; + break; + + case binop_mul: + assert(node->datatype == dt_double); + res->v_double = a.v_double * b.v_double; + break; + + case binop_div: + assert(node->datatype == dt_double); + res->v_double = a.v_double / b.v_double; + break; + + case binop_eq: + EVAL_COMP(==, node, a, b, res); + break; + + case binop_ne: + EVAL_COMP(!=, node, a, b, res); + break; + + case binop_gt: + EVAL_COMP(>, node, a, b, res); + break; + + case binop_ge: + EVAL_COMP(>=, node, a, b, res); + break; + + case binop_lt: + EVAL_COMP(<, node, a, b, res); + break; + + case binop_le: + EVAL_COMP(<=, node, a, b, res); + break; + + case binop_and: + assert(node->datatype == dt_bool); + res->v_bool = a.v_bool && b.v_bool; + break; + + case binop_or: + assert(node->datatype == dt_bool); + res->v_bool = a.v_bool || b.v_bool; + break; + } + break; + + case node_unary: + node_eval(node->v.unary.operand, env, &a); + switch (node->v.unary.opcode) + { + case unop_not: + assert(node->datatype == dt_bool); + res->v_bool = !a.v_bool; + break; + + case unop_minus: + assert(node->datatype == dt_double); + res->v_double = - a.v_double; + break; + + case unop_typecast: + switch (node->datatype) { + case dt_int: + res->v_int = a.v_bool; + break; + case dt_double: + switch (node->v.unary.operand->datatype) { + case dt_int: + res->v_double = a.v_int; + break; + case dt_bool: + res->v_double = a.v_bool; + break; + default: + abort(); + } + default: + abort(); + } + } + break; + } +} + +int +expr_eval(int argc, char **argv, IFSTAT a, IFSTAT b) +{ + struct eval_env env; + union eval_res res; + + lex_init(argc, argv); + if (yyparse()) + exit(1); + memcpy(env.stat[0], a, sizeof a); + memcpy(env.stat[1], b, sizeof b); + node_eval(parse_tree, &env, &res); + return res.v_bool; +} diff --git a/extra/ifalive.c b/extra/ifalive.c index 8453b74..1fbc8d4 100644 --- a/extra/ifalive.c +++ b/extra/ifalive.c @@ -7,6 +7,7 @@ #include <stdint.h> #include <signal.h> #include <assert.h> +#include "ifalive.h" #ifndef PATH_PROCNET_DEV # define PATH_PROCNET_DEV "/proc/net/dev" @@ -41,7 +42,7 @@ usage(int code) FILE *fp = code ? stderr : stdout; fprintf(fp, "Usage: ifalive -i [-dnp] IFACE [PID]\n" - " ifalive [-c] [-dn] [-s SIG] PID\n" + " ifalive [-c] [-dn] [-s SIG] PID [EXPR...]\n" " ifalive -r [-dnp] [PID]\n" " ifalive -h\n" "\n" @@ -72,27 +73,6 @@ trimnl(char *s) return n; } -enum { - stat_rx_bytes, - stat_rx_packets, - stat_rx_errors, - stat_rx_dropped, - stat_rx_fifo_errors, - stat_rx_frame_errors, - stat_rx_compressed, - stat_rx_multicast, - stat_tx_bytes, - stat_tx_packets, - stat_tx_errors, - stat_tx_dropped, - stat_tx_fifo_errors, - stat_collisions, - stat_tx_carrier_errors, - stat_tx_compressed, - - MAX_STATS -}; - static int getnum(uintmax_t *ret, char **pptr) { @@ -298,20 +278,36 @@ ifalive_remove(int argc, char **argv) return EX_OK; } +static int def_argc = 1; +static char *def_argv[] = { + "a.rx_bytes != b.rx_bytes and a.tx_bytes != b.tx_bytes", + NULL +}; + int ifalive_check(int argc, char **argv) { FILE *fp; char buf[512]; char *iface; + char *pidstr; uintmax_t statbuf[MAX_STATS], snapshot[MAX_STATS]; - if (argc != 1) + if (argc == 0) usage(EX_FAIL); - fp = fopen(argv[0], "r+"); + pidstr = argv[0]; + argc--; + argv++; + + if (argc == 0) { + argc = def_argc; + argv = def_argv; + } + + fp = fopen(pidstr, "r+"); if (!fp) { - error(errno, "can't open \"%s/%s\"", spooldir, argv[0]); + error(errno, "can't open \"%s/%s\"", spooldir, pidstr); return EX_FAIL; } @@ -325,7 +321,7 @@ ifalive_check(int argc, char **argv) iface = buf; memset(snapshot, 0, sizeof snapshot); } else if (procnet_parse_line(buf, &iface, snapshot)) { - error(0, "%s/%s: malformed snapshot", spooldir, argv[0]); + error(0, "%s/%s: malformed snapshot", spooldir, pidstr); return EX_FAIL; } @@ -335,13 +331,12 @@ ifalive_check(int argc, char **argv) if (procnet_read(iface, statbuf)) return EX_FAIL; - if (snapshot[stat_rx_bytes] == statbuf[stat_rx_bytes] - && snapshot[stat_tx_bytes] == statbuf[stat_tx_bytes]) { + if (expr_eval(argc, argv, snapshot, statbuf) == 0) { if (debug) - printf("%s is IDLE; terminate %s\n", iface, argv[0]); + printf("%s is IDLE; terminate %s\n", iface, pidstr); if (!dry_run) { char *endp; - pid_t pid = strtoul(argv[0], &endp, 10); + pid_t pid = strtoul(pidstr, &endp, 10); assert(pid >= 2); kill(pid, sig); } diff --git a/extra/ifalive.h b/extra/ifalive.h new file mode 100644 index 0000000..da070fa --- /dev/null +++ b/extra/ifalive.h @@ -0,0 +1,28 @@ +enum { + stat_rx_bytes, + stat_rx_packets, + stat_rx_errors, + stat_rx_dropped, + stat_rx_fifo_errors, + stat_rx_frame_errors, + stat_rx_compressed, + stat_rx_multicast, + stat_tx_bytes, + stat_tx_packets, + stat_tx_errors, + stat_tx_dropped, + stat_tx_fifo_errors, + stat_collisions, + stat_tx_carrier_errors, + stat_tx_compressed, + + MAX_STATS +}; + +enum { + VAR_PREV, + VAR_THIS +}; + +typedef uintmax_t IFSTAT[MAX_STATS]; +int expr_eval(int argc, char **argv, IFSTAT a, IFSTAT b); diff --git a/extra/lex.l b/extra/lex.l new file mode 100644 index 0000000..2023439 --- /dev/null +++ b/extra/lex.l @@ -0,0 +1,98 @@ +%{ +#include <config.h> +#include <stdlib.h> +#include <stdint.h> +#include "expr.h" +#include "ifalive.h" + +#define RET_STAT(s) { yylval.field = s; return FIELD; } + +#undef YY_INPUT +#define YY_INPUT(buf,result,max_size) result = fillbuf(buf, max_size) + +static int lex_argc; +static char **lex_argv; +static int lex_argi; +static char *lex_curp; + +static size_t +fillbuf(char *buf, size_t max_size) +{ + size_t consumed = 0; + + while (consumed < max_size) { + size_t n; + + if (!*lex_curp) { + if (lex_argi == lex_argc) + break; + lex_curp = lex_argv[lex_argi++]; + } + n = strlen(lex_curp); + if (n > max_size) + n = max_size; + memcpy(buf + consumed, lex_curp, n); + lex_curp += n; + consumed += n; + } + + return consumed; +} + +void +lex_init(int argc, char **argv) +{ + lex_argc = argc; + lex_argv = argv; + lex_argi = 1; + lex_curp = argv[0]; +} + +void +yyerror(char const *s) +{ + if (lex_curp) + error(0, "%s (near %s)", s, lex_curp); + else + error(0, "%s (at the end of input)", s); +} + +static int +yywrap(void) +{ + return 1; +} +%} +%% +"or" return OR; +"and" return AND; +"=" return EQ; +"==" return EQ; +"!=" return NE; +"<" return LT; +"<=" return LE; +">" return GT; +">=" return GE; +"!" return NOT; +rx_bytes RET_STAT(stat_rx_bytes); +rx_packets RET_STAT(stat_rx_packets); +rx_errors RET_STAT(stat_rx_errors); +rx_dropped RET_STAT(stat_rx_dropped); +rx_fifo_errors RET_STAT(stat_rx_fifo_errors); +rx_frame_errors RET_STAT(stat_rx_frame_errors); +rx_compressed RET_STAT(stat_rx_compressed); +rx_multicast RET_STAT(stat_rx_multicast); +tx_bytes RET_STAT(stat_tx_bytes); +tx_packets RET_STAT(stat_tx_packets); +tx_errors RET_STAT(stat_tx_errors); +tx_dropped RET_STAT(stat_tx_dropped); +tx_fifo_errors RET_STAT(stat_tx_fifo_errors); +collisions RET_STAT(stat_collisions); +tx_carrier_errors RET_STAT(stat_tx_carrier_errors); +tx_compressed RET_STAT(stat_tx_compressed); +a { yylval.var = VAR_PREV; return VAR; } +b { yylval.var = VAR_THIS; return VAR; } +[0-9][0-9]* { yylval.value = strtoumax(yytext, NULL, 10); + return VALUE; } +[ \t\n] ; +. return yytext[0]; |