// -*- c-basic-offset: 4 -*-
/*
* click.cc -- user-level Click main program
* Eddie Kohler
*
* Copyright (c) 1999-2000 Massachusetts Institute of Technology
* Copyright (c) 2000 Mazu Networks, Inc.
* Copyright (c) 2001-2003 International Computer Science Institute
* Copyright (c) 2004-2006 Regents of the University of California
*
* 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 <click/pathvars.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <fcntl.h>
#include <click/lexer.hh>
#include <click/routerthread.hh>
#include <click/router.hh>
#include <click/master.hh>
#include <click/error.hh>
#include <click/timer.hh>
#include <click/straccum.hh>
#include <click/clp.h>
#include <click/archive.hh>
#include <click/glue.hh>
#include <click/driver.hh>
#include <click/userutils.hh>
#include <click/confparse.hh>
#include <click/handlercall.hh>
#include "elements/standard/quitwatcher.hh"
#include "elements/userlevel/controlsocket.hh"
CLICK_USING_DECLS
#define HELP_OPT 300
#define VERSION_OPT 301
#define CLICKPATH_OPT 302
#define ROUTER_OPT 303
#define EXPRESSION_OPT 304
#define QUIT_OPT 305
#define OUTPUT_OPT 306
#define HANDLER_OPT 307
#define TIME_OPT 308
#define PORT_OPT 310
#define UNIX_SOCKET_OPT 311
#define NO_WARNINGS_OPT 312
#define WARNINGS_OPT 313
#define ALLOW_RECONFIG_OPT 314
#define EXIT_HANDLER_OPT 315
static Clp_Option options[] = {
{ "allow-reconfigure", 'R', ALLOW_RECONFIG_OPT, 0, Clp_Negate },
{ "clickpath", 'C', CLICKPATH_OPT, Clp_ArgString, 0 },
{ "expression", 'e', EXPRESSION_OPT, Clp_ArgString, 0 },
{ "file", 'f', ROUTER_OPT, Clp_ArgString, 0 },
{ "handler", 'h', HANDLER_OPT, Clp_ArgString, 0 },
{ "help", 0, HELP_OPT, 0, 0 },
{ "output", 'o', OUTPUT_OPT, Clp_ArgString, 0 },
{ "port", 'p', PORT_OPT, Clp_ArgInt, 0 },
{ "quit", 'q', QUIT_OPT, 0, 0 },
{ "time", 't', TIME_OPT, 0, 0 },
{ "unix-socket", 'u', UNIX_SOCKET_OPT, Clp_ArgString, 0 },
{ "version", 'v', VERSION_OPT, 0, 0 },
{ "warnings", 0, WARNINGS_OPT, 0, Clp_Negate },
{ "exit-handler", 'x', EXIT_HANDLER_OPT, Clp_ArgString, 0 },
{ 0, 'w', NO_WARNINGS_OPT, 0, Clp_Negate },
};
static const char *program_name;
void
short_usage()
{
fprintf(stderr, "Usage: %s [OPTION]... [ROUTERFILE]\n\
Try '%s --help' for more information.\n",
program_name, program_name);
}
void
usage()
{
printf("\
'Click' runs a Click router configuration at user level. It installs the\n\
configuration, reporting any errors to standard error, and then generally runs\n\
until interrupted.\n\
\n\
Usage: %s [OPTION]... [ROUTERFILE]\n\
\n\
Options:\n\
-f, --file FILE Read router configuration from FILE.\n\
-e, --expression EXPR Use EXPR as router configuration.\n\
-p, --port PORT Listen for control connections on TCP port.\n\
-u, --unix-socket FILE Listen for control connections on Unix socket.\n\
-R, --allow-reconfigure Provide a writable 'hotconfig' handler.\n\
-h, --handler ELEMENT.H Call ELEMENT's read handler H after running\n\
driver and print result to standard output.\n\
-x, --exit-handler ELEMENT.H Use handler ELEMENT.H value for exit status.\n\
-o, --output FILE Write flat configuration to FILE.\n\
-q, --quit Do not run driver.\n\
-t, --time Print information on how long driver took.\n\
-w, --no-warnings Do not print warnings.\n\
-C, --clickpath PATH Use PATH for CLICKPATH.\n\
--help Print this message and exit.\n\
-v, --version Print version number and exit.\n\
\n\
Report bugs to <click@pdos.lcs.mit.edu>.\n", program_name);
}
static Router *router;
static ErrorHandler *errh;
static bool started = 0;
extern "C" {
static void
catch_signal(int sig)
{
signal(sig, SIG_DFL);
if (!started)
kill(getpid(), sig);
else
router->set_runcount(Router::STOP_RUNCOUNT);
}
}
// report handler results
static int
call_read_handler(Element *e, String handler_name,
bool print_name, ErrorHandler *errh)
{
const Handler *rh = Router::handler(e, handler_name);
String full_name = Handler::unparse_name(e, handler_name);
if (!rh || !rh->visible())
return errh->error("no '%s' handler", full_name.c_str());
else if (!rh->read_visible())
return errh->error("'%s' is a write handler", full_name.c_str());
if (print_name)
fprintf(stdout, "%s:\n", full_name.c_str());
String result = rh->call_read(e);
if (!rh->raw() && result && result.back() != '\n')
result += '\n';
fputs(result.c_str(), stdout);
if (print_name)
fputs("\n", stdout);
return 0;
}
static int
expand_handler_elements(const String& pattern, const String& handler_name,
Vector<Element*>& elements, Router* router)
{
// first try element name
if (Element* e = router->find(pattern)) {
elements.push_back(e);
return 1;
}
// check if we have a pattern
bool is_pattern = false;
for (const char* s = pattern.begin(); s < pattern.end(); s++)
if (*s == '?' || *s == '*' || *s == '[') {
is_pattern = true;
break;
}
// check pattern or type
bool any = false;
for (int i = 0; i < router->nelements(); i++)
if (is_pattern
? glob_match(router->ename(i), pattern)
: router->element(i)->cast(pattern.c_str()) != 0) {
any = true;
const Handler* h = Router::handler(router->element(i), handler_name);
if (h && h->read_visible())
elements.push_back(router->element(i));
}
if (!any)
return errh->error((is_pattern ? "no element matching '%s'" : "no element '%s'"), pattern.c_str());
else
return 2;
}
static int
call_read_handlers(Vector<String> &handlers, ErrorHandler *errh)
{
Vector<Element *> handler_elements;
Vector<String> handler_names;
bool print_names = (handlers.size() > 1);
int before = errh->nerrors();
// expand handler names
for (int i = 0; i < handlers.size(); i++) {
const char *dot = find(handlers[i], '.');
if (dot == handlers[i].end()) {
call_read_handler(router->root_element(), handlers[i], print_names, errh);
continue;
}
String element_name = handlers[i].substring(handlers[i].begin(), dot);
String handler_name = handlers[i].substring(dot + 1, handlers[i].end());
Vector<Element*> elements;
int retval = expand_handler_elements(element_name, handler_name, elements, router);
if (retval >= 0)
for (int j = 0; j < elements.size(); j++)
call_read_handler(elements[j], handler_name, print_names || retval > 1, errh);
}
return (errh->nerrors() == before ? 0 : -1);
}
// hotswapping
static Router *hotswap_router;
static Router *hotswap_thunk_router;
static bool hotswap_hook(Task *, void *);
static Task hotswap_task(hotswap_hook, 0);
static bool
hotswap_hook(Task *, void *)
{
hotswap_router->activate(ErrorHandler::default_handler());
router->unuse();
router = hotswap_router;
router->use();
hotswap_router = 0;
return true;
}
// switching configurations
static Vector<String> cs_unix_sockets;
static Vector<int> cs_ports;
static bool warnings = true;
static Router *
parse_configuration(const String &text, bool text_is_expr, bool hotswap,
ErrorHandler *errh)
{
Router *r = click_read_router(text, text_is_expr, errh, false, (router ? router->master() : 0));
if (!r)
return 0;
// add new ControlSockets
String retries = (hotswap ? ", RETRIES 1, RETRY_WARNINGS false" : "");
for (int i = 0; i < cs_ports.size(); i++)
r->add_element(new ControlSocket, "click_driver@@ControlSocket@" + String(i), "tcp, " + String(cs_ports[i]) + retries, "click");
for (int i = 0; i < cs_unix_sockets.size(); i++)
r->add_element(new ControlSocket, "click_driver@@ControlSocket@" + String(i + cs_ports.size()), "unix, " + cp_quote(cs_unix_sockets[i]) + retries, "click");
// catch signals (only need to do the first time)
if (!hotswap) {
// catch control-C and SIGTERM
signal(SIGINT, catch_signal);
signal(SIGTERM, catch_signal);
// ignore SIGPIPE
signal(SIGPIPE, SIG_IGN);
}
// register hotswap router on new router
if (hotswap && router && router->initialized())
r->set_hotswap_router(router);
if (errh->nerrors() > 0 || r->initialize(errh) < 0) {
delete r;
return 0;
} else
return r;
}
static int
hotconfig_handler(const String &text, Element *, void *, ErrorHandler *errh)
{
if (Router *q = parse_configuration(text, true, true, errh)) {
if (hotswap_router)
hotswap_router->unuse();
hotswap_router = q;
hotswap_task.reschedule();
return 0;
} else
return -EINVAL;
}
// main
int
main(int argc, char **argv)
{
click_static_initialize();
errh = ErrorHandler::default_handler();
// read command line arguments
Clp_Parser *clp =
Clp_NewParser(argc, argv, sizeof(options) / sizeof(options[0]), options);
program_name = Clp_ProgramName(clp);
const char *router_file = 0;
bool file_is_expr = false;
const char *output_file = 0;
bool quit_immediately = false;
bool report_time = false;
bool allow_reconfigure = false;
Vector<String> handlers;
String exit_handler;
while (1) {
int opt = Clp_Next(clp);
switch (opt) {
case ROUTER_OPT:
case EXPRESSION_OPT:
case Clp_NotOption:
if (router_file) {
errh->error("router configuration specified twice");
goto bad_option;
}
router_file = clp->arg;
file_is_expr = (opt == EXPRESSION_OPT);
break;
case OUTPUT_OPT:
if (output_file) {
errh->error("output file specified twice");
goto bad_option;
}
output_file = clp->arg;
break;
case HANDLER_OPT:
handlers.push_back(clp->arg);
break;
case EXIT_HANDLER_OPT:
if (exit_handler) {
errh->error("--exit-handler specified twice");
goto bad_option;
}
exit_handler = clp->arg;
break;
case PORT_OPT:
cs_ports.push_back(clp->val.i);
break;
case UNIX_SOCKET_OPT:
cs_unix_sockets.push_back(clp->arg);
break;
case ALLOW_RECONFIG_OPT:
allow_reconfigure = !clp->negated;
break;
case QUIT_OPT:
quit_immediately = true;
break;
case TIME_OPT:
report_time = true;
break;
case WARNINGS_OPT:
warnings = !clp->negated;
break;
case NO_WARNINGS_OPT:
warnings = clp->negated;
break;
case CLICKPATH_OPT:
set_clickpath(clp->arg);
break;
case HELP_OPT:
usage();
exit(0);
break;
case VERSION_OPT:
printf("click (Click) %s\n", CLICK_VERSION);
printf("Copyright (C) 1999-2001 Massachusetts Institute of Technology\n\
Copyright (C) 2001-2003 International Computer Science Institute\n\
Copyright (C) 2004-2006 Regents of the University of California\n\
This is free software; see the source for copying conditions.\n\
There is NO warranty, not even for merchantability or fitness for a\n\
particular purpose.\n");
exit(0);
break;
bad_option:
case Clp_BadOption:
short_usage();
exit(1);
break;
case Clp_Done:
goto done;
}
}
done:
// provide hotconfig handler if asked
if (allow_reconfigure) {
Router::add_write_handler(0, "hotconfig", hotconfig_handler, 0);
Router::change_handler_flags(0, "hotconfig", 0, Handler::RAW);
}
// parse configuration
router = parse_configuration(router_file, file_is_expr, false, errh);
if (!router)
exit(1);
router->use();
int exit_value = 0;
// output flat configuration
if (output_file) {
FILE *f = 0;
if (strcmp(output_file, "-") != 0) {
f = fopen(output_file, "w");
if (!f) {
errh->error("%s: %s", output_file, strerror(errno));
exit_value = 1;
}
} else
f = stdout;
if (f) {
Element *root = router->root_element();
String s = Router::handler(root, "flatconfig")->call_read(root);
fwrite(s.data(), 1, s.length(), f);
if (f != stdout)
fclose(f);
}
}
struct rusage before, after;
struct timeval before_time, after_time;
getrusage(RUSAGE_SELF, &before);
gettimeofday(&before_time, 0);
// run driver
// 10.Apr.2004 - Don't run the router if it has no elements.
if (!quit_immediately && router->nelements()) {
started = true;
router->activate(errh);
if (allow_reconfigure) {
hotswap_thunk_router = new Router("", router->master());
hotswap_thunk_router->initialize(errh);
hotswap_task.initialize(hotswap_thunk_router, false);
hotswap_thunk_router->activate(false, errh);
}
router->master()->thread(0)->driver();
} else if (!quit_immediately && warnings)
errh->warning("%s: configuration has no elements, exiting", filename_landmark(router_file, file_is_expr));
gettimeofday(&after_time, 0);
getrusage(RUSAGE_SELF, &after);
// report time
if (report_time) {
struct timeval diff;
timersub(&after.ru_utime, &before.ru_utime, &diff);
printf("%ld.%03ldu", (long)diff.tv_sec, (long)((diff.tv_usec+500)/1000));
timersub(&after.ru_stime, &before.ru_stime, &diff);
printf(" %ld.%03lds", (long)diff.tv_sec, (long)((diff.tv_usec+500)/1000));
timersub(&after_time, &before_time, &diff);
printf(" %ld:%02ld.%02ld", (long)(diff.tv_sec/60), (long)(diff.tv_sec%60), (long)((diff.tv_usec+5000)/10000));
printf("\n");
}
// call handlers
if (handlers.size())
if (call_read_handlers(handlers, errh) < 0)
exit_value = 1;
// call exit handler
if (exit_handler) {
int before = errh->nerrors();
String exit_string = HandlerCall::call_read(exit_handler, router->root_element(), errh);
bool b;
if (errh->nerrors() != before)
exit_value = -1;
else if (cp_integer(cp_uncomment(exit_string), &exit_value))
/* nada */;
else if (cp_bool(cp_uncomment(exit_string), &b))
exit_value = (b ? 0 : 1);
else {
errh->error("exit handler value should be integer");
exit_value = -1;
}
}
Master *master = router->master();
router->unuse();
delete master;
click_static_cleanup();
Clp_DeleteParser(clp);
exit(exit_value);
// Will leave String objects allocated because of 'handlers' and
// 'exit_handler' above
}
syntax highlighted by Code2HTML, v. 0.9.1