📄 script.cc
字号:
// -*- c-basic-offset: 4 -*-/* * script.{cc,hh} -- element provides scripting functionality * Eddie Kohler * * Copyright (c) 2001 International Computer Science Institute * Copyright (c) 2001 Mazu Networks, Inc. * Copyright (c) 2005-2008 Regents of the University of California * Copyright (c) 2008-2009 Meraki, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, subject to the conditions * listed in the Click LICENSE file. These conditions include: you must * preserve this copyright notice, and you cannot mention the copyright * holders in advertising related to the Software without their permission. * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This * notice is a summary of the Click LICENSE file; the license in that file is * legally binding. */#include <click/config.h>#include "script.hh"#include <click/confparse.hh>#include <click/error.hh>#include <click/router.hh>#include <click/straccum.hh>#include <click/handlercall.hh>#include <click/nameinfo.hh>#if CLICK_USERLEVEL# include <signal.h># include <click/master.hh># include <click/userutils.hh>#endifCLICK_DECLSstatic const StaticNameDB::Entry instruction_entries[] = { { "end", Script::INSN_END }, { "error", Script::insn_error }, { "errorq", Script::insn_errorq }, { "exit", Script::INSN_EXIT }, { "goto", Script::INSN_GOTO }, { "init", Script::INSN_INIT }, { "initq", Script::insn_initq }, { "label", Script::INSN_LABEL }, { "loop", Script::INSN_LOOP_PSEUDO }, { "pause", Script::INSN_WAIT_STEP }, { "print", Script::INSN_PRINT }, { "printn", Script::INSN_PRINTN }, { "read", Script::INSN_READ }, { "readq", Script::INSN_READQ }, { "return", Script::INSN_RETURN }, { "returnq", Script::insn_returnq }, { "set", Script::INSN_SET }, { "setq", Script::insn_setq }, { "stop", Script::INSN_STOP }, { "wait", Script::INSN_WAIT_PSEUDO }, { "wait_for", Script::INSN_WAIT_TIME }, { "wait_step", Script::INSN_WAIT_STEP }, { "wait_stop", Script::INSN_WAIT_STEP }, { "wait_time", Script::INSN_WAIT_TIME }, { "write", Script::INSN_WRITE }, { "writeq", Script::INSN_WRITEQ }};#if CLICK_USERLEVELstatic const StaticNameDB::Entry signal_entries[] = { { "ABRT", SIGABRT }, { "HUP", SIGHUP }, { "INT", SIGINT }, { "PIPE", SIGPIPE }, { "QUIT", SIGQUIT }, { "TERM", SIGTERM }, { "TSTP", SIGTSTP }, { "USR1", SIGUSR1 }, { "USR2", SIGUSR2 }};#endifstatic NameDB *dbs[2];voidScript::static_initialize(){ dbs[0] = new StaticNameDB(NameInfo::T_SCRIPT_INSN, String(), instruction_entries, sizeof(instruction_entries) / sizeof(instruction_entries[0])); NameInfo::installdb(dbs[0], 0);#if CLICK_USERLEVEL dbs[1] = new StaticNameDB(NameInfo::T_SIGNO, String(), signal_entries, sizeof(signal_entries) / sizeof(signal_entries[0])); NameInfo::installdb(dbs[1], 0);#endif}voidScript::static_cleanup(){ delete dbs[0];#if CLICK_USERLEVEL delete dbs[1];#endif}Script::Script() : _type(type_active), _write_status(0), _timer(this), _cur_steps(0){}Script::~Script(){}voidScript::add_insn(int insn, int arg, int arg2, const String &arg3){ // first instruction must be WAIT or WAIT_STEP, so add INITIAL if // necessary if (_insns.size() == 0 && insn > INSN_WAIT_TIME) add_insn(INSN_INITIAL, 0); _insns.push_back(insn); _args.push_back(arg); _args2.push_back(arg2); _args3.push_back(arg3);}intScript::find_label(const String &label) const{ for (int i = 0; i < _insns.size(); i++) if (_insns[i] == INSN_LABEL && _args3[i] == label) return i; if (label.equals("exit", 4)) return LABEL_EXIT; if (label.equals("end", 3)) return LABEL_END; if (label.equals("begin", 5)) return LABEL_BEGIN; if (label.equals("error", 5)) return label_error; return _insns.size();}intScript::find_variable(const String &name, bool add){ int i; for (i = 0; i < _vars.size(); i += 2) if (_vars[i] == name) goto found; if (add) { _vars.push_back(name); _vars.push_back(String()); } found: return i;}intScript::configure(Vector<String> &conf, ErrorHandler *errh){ String type_arg; if (cp_va_kparse_remove_keywords (conf, this, errh, "TYPE", 0, cpArgument, &type_arg, cpEnd) < 0) return -1; int before = errh->nerrors(); String type_word = cp_shift_spacevec(type_arg); if (type_word == "ACTIVE" && !type_arg) _type = type_active; else if (type_word == "PASSIVE" && !type_arg) _type = type_passive; else if ((type_word == "PACKET" || type_word == "PUSH") && !type_arg) _type = type_push; else if (type_word == "PROXY" && !type_arg) _type = type_proxy; else if (type_word == "DRIVER" && !type_arg) _type = type_driver;#if CLICK_USERLEVEL else if (type_word == "SIGNAL") { _type = type_signal; int32_t signo; while ((type_word = cp_shift_spacevec(type_arg)) && NameInfo::query_int(NameInfo::T_SIGNO, this, type_word, &signo) && signo >= 0 && signo < 32) _signos.push_back(signo); if (type_word || !_signos.size()) return errh->error("bad signal number"); }#endif else if (type_word) return errh->error("bad TYPE"); if (_type == type_driver) { if (router()->attachment("Script")) return errh->error("router has more than one driver script"); router()->set_attachment("Script", this); } if (_type == type_push && ninputs() == 0) return errh->error("PACKET script must have inputs"); else if (_type != type_push && (ninputs() || noutputs())) return errh->error("ports allowed only on PACKET scripts"); for (int i = 0; i < conf.size(); i++) { String insn_name = cp_shift_spacevec(conf[i]); int32_t insn; if (!insn_name) // ignore as benign continue; else if (!NameInfo::query_int(NameInfo::T_SCRIPT_INSN, this, insn_name, &insn)) { errh->error("syntax error at '%s'", insn_name.c_str()); continue; } switch (insn) { case INSN_WAIT_PSEUDO: if (!conf[i]) goto wait_step; else goto wait_time; wait_step: case INSN_WAIT_STEP: { unsigned n = 1; if (!conf[i] || cp_integer(conf[i], &n)) add_insn(INSN_WAIT_STEP, n, 0); else goto syntax_error; break; } case INSN_WRITE: case INSN_WRITEQ: case INSN_READ: case INSN_READQ: case INSN_PRINT: case INSN_PRINTN: case INSN_GOTO: add_insn(insn, 0, 0, conf[i]); break; case INSN_RETURN: case insn_returnq: conf[i] = "_ " + conf[i]; /* fall through */ case INSN_INIT: case insn_initq: case INSN_SET: case insn_setq: { String word = cp_shift_spacevec(conf[i]); if (!word || ((insn == INSN_SET || insn == insn_setq) && !conf[i])) goto syntax_error; add_insn(insn, find_variable(word, true), 0, conf[i]); break; } wait_time: case INSN_WAIT_TIME: add_insn(INSN_WAIT_TIME, 0, 0, conf[i]); break; case INSN_LABEL: { String word = cp_shift_spacevec(conf[i]); if (!word || conf[i]) goto syntax_error; add_insn(insn, 0, 0, word); break; } case INSN_LOOP_PSEUDO: insn = INSN_GOTO; /* fallthru */ case INSN_END: case INSN_EXIT: case INSN_STOP: case insn_error: case insn_errorq: if (conf[i] && insn != insn_error && insn != insn_errorq) goto syntax_error; add_insn(insn, 0, 0, conf[i]); break; default: syntax_error: errh->error("syntax error at '%s'", insn_name.c_str()); break; } } // fix the gotos for (int i = 0; i < _insns.size(); i++) if (_insns[i] == INSN_GOTO && _args3[i]) { String word = cp_shift_spacevec(_args3[i]); if ((_args[i] = find_label(word)) >= _insns.size()) errh->error("no such label '%s'", word.c_str()); } if (_insns.size() == 0 && _type == type_driver) add_insn(INSN_WAIT_STEP, 1, 0); add_insn(_type == type_driver ? INSN_STOP : INSN_END, 0); return (errh->nerrors() == before ? 0 : -1);}intScript::initialize(ErrorHandler *errh){ _insn_pos = 0; _step_count = 0; _timer.initialize(this); Expander expander; expander.script = this; expander.errh = errh; for (int i = 0; i < _insns.size(); i++) if (_insns[i] == INSN_INIT) _vars[_args[i] + 1] = cp_expand(_args3[i], expander); else if (_insns[i] == insn_initq) _vars[_args[i] + 1] = cp_unquote(cp_expand(_args3[i], expander)); int insn = _insns[_insn_pos]; assert(insn <= INSN_WAIT_TIME); if (_type == type_signal || _type == type_passive || _type == type_push || _type == type_proxy) /* passive, do nothing */; else if (insn == INSN_WAIT_TIME) { Timestamp ts; if (cp_time(cp_expand(_args3[_insn_pos], expander), &ts)) _timer.schedule_after(ts); else errh->error("syntax error at 'wait'"); } else if (insn == INSN_INITIAL) { // get rid of the initial runcount so we get called right away if (_type == type_driver) router()->adjust_runcount(-1); else _timer.schedule_now(); _args[0] = 1; }#if CLICK_USERLEVEL if (_type == type_signal) for (int i = 0; i < _signos.size(); i++) master()->add_signal_handler(_signos[i], router(), name() + ".run");#endif return 0;}intScript::step(int nsteps, int step_type, int njumps, ErrorHandler *errh){ Expander expander; expander.script = this; expander.errh = errh; nsteps += _step_count; while ((nsteps - _step_count) >= 0 && _insn_pos < _insns.size() && njumps < max_jumps) { // process current instruction // increment instruction pointer now, in case we call 'goto' directly // or indirectly int ipos = _insn_pos++; int insn = _insns[ipos]; switch (insn) { case INSN_STOP: _step_count++; _insn_pos--; if (step_type != STEP_ROUTER) router()->adjust_runcount(-1); return njumps + 1; case INSN_WAIT_STEP: while (_step_count < nsteps && _args2[ipos] < _args[ipos]) { _args2[ipos]++; _step_count++; } if (_step_count == nsteps && _args2[ipos] < _args[ipos]) { _insn_pos--; goto done; } break; case INSN_WAIT_TIME: if (_step_count == nsteps) { Timestamp ts; if (cp_time(cp_expand(_args3[ipos], expander), &ts)) { _timer.schedule_after(ts); _insn_pos--; } else errh->error("syntax error at 'wait'"); goto done; } _step_count++; _timer.unschedule(); break; case INSN_INITIAL: if (_args[ipos]) { _step_count++; _args[ipos] = 0; } break; case INSN_PRINT: case INSN_PRINTN: { String text = _args3[ipos];#if CLICK_USERLEVEL || CLICK_TOOL FILE *f = stdout; if (text.length() && text[0] == '>') { bool append = (text.length() > 1 && text[1] == '>'); text = text.substring(1 + append); String filename = cp_shift_spacevec(text); if (filename && filename != "-" && !(f = fopen(filename.c_str(), append ? "ab" : "wb"))) { errh->error("%s: %s", filename.c_str(), strerror(errno)); break; } }#endif int before = errh->nerrors(); String result; if (text && (isalpha((unsigned char) text[0]) || text[0] == '@' || text[0] == '_')) { result = cp_expand(text, expander); result = HandlerCall::call_read(result, this, errh); } else result = cp_unquote(cp_expand(text, expander, true)); if (errh->nerrors() == before && (!result || result.back() != '\n') && insn != INSN_PRINTN) result += "\n";#if CLICK_USERLEVEL || CLICK_TOOL ignore_result(fwrite(result.data(), 1, result.length(), f)); if (f == stdout) fflush(f); else fclose(f);#else click_chatter("{}%s", result.c_str());#endif break; } case INSN_READ: case INSN_READQ: { String arg = (insn == INSN_READ ? _args3[ipos] : cp_unquote(_args3[ipos])); HandlerCall hc(cp_expand(arg, expander)); if (hc.initialize_read(this, errh) >= 0) { ContextErrorHandler c_errh(errh, "While calling %<%s%>:", hc.unparse().c_str()); String result = hc.call_read(&c_errh); ErrorHandler *d_errh = ErrorHandler::default_handler(); d_errh->message("%s:\n%s\n", hc.handler()->unparse_name(hc.element()).c_str(), result.c_str()); } break; } case INSN_WRITE: case INSN_WRITEQ: { String arg = (insn == INSN_WRITE ? _args3[ipos] : cp_unquote(_args3[ipos])); HandlerCall hc(cp_expand(arg, expander)); if (hc.initialize_write(this, errh) >= 0) { ContextErrorHandler c_errh(errh, "While calling %<%s%>:", hc.unparse().c_str()); _write_status = hc.call_write(&c_errh); } break; } case INSN_RETURN: case insn_returnq: case INSN_SET: case insn_setq: { expander.errh = errh; _vars[_args[ipos] + 1] = cp_expand(_args3[ipos], expander); if (insn == insn_setq || insn == insn_returnq) _vars[_args[ipos] + 1] = cp_unquote(_vars[_args[ipos] + 1]); if ((insn == INSN_RETURN || insn == insn_returnq) && _insn_pos == ipos + 1) { _insn_pos--; goto done; } break; } case INSN_GOTO: { // reset intervening instructions String cond_text = cp_expand(_args3[ipos], expander); bool cond; if (cond_text && !cp_bool(cond_text, &cond)) errh->error("bad condition '%s'", cond_text.c_str()); else if (!cond_text || cond) { if (_args[ipos] < 0) goto insn_finish; for (int i = _args[ipos]; i < ipos; i++) if (_insns[i] == INSN_WAIT_STEP) _args2[i] = 0; _insn_pos = _args[ipos]; } break; } case insn_error: case insn_errorq: { String msg = cp_expand(_args3[ipos], expander); if (insn == insn_errorq) msg = cp_unquote(msg); if (msg) errh->error("%s", msg.c_str()); /* fallthru */ } case INSN_END: case INSN_EXIT: insn_finish: _insn_pos--; return njumps + 1; } if (_insn_pos != ipos + 1) njumps++; } done: if (njumps >= max_jumps) { ErrorHandler::default_handler()->error("%{element}: too many jumps, giving up", this); _insn_pos = _insns.size(); _timer.unschedule(); } if (step_type == STEP_ROUTER) router()->adjust_runcount(1); return njumps + 1;}intScript::complete_step(String *retval){ int last_insn; if (_insn_pos < 0 || _insn_pos >= _insns.size()) last_insn = -1; else { last_insn = _insns[_insn_pos]; if (last_insn == INSN_GOTO) { if (_args[_insn_pos] == LABEL_EXIT) last_insn = INSN_EXIT; else if (_args[_insn_pos] == LABEL_END) last_insn = INSN_END;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -