aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2017-02-27 08:56:17 +0200
committerSergey Poznyakoff <gray@gnu.org>2017-02-27 08:56:17 +0200
commitfafe2d8d6eecd2272aabe65bdfff9d704c8548b7 (patch)
tree6d0351860ef82e58d0a704bdc13accf1bab35ef9
parenteb00a46f8eb47a31bb4d4c5c4af72153239103e3 (diff)
downloadjumper-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/.gitignore5
-rw-r--r--extra/Makefile.am7
-rw-r--r--extra/expr.y415
-rw-r--r--extra/ifalive.c55
-rw-r--r--extra/ifalive.h28
-rw-r--r--extra/lex.l98
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];

Return to:

Send suggestions and report system problems to the System administrator.