/*
* khandlerproxy.{cc,hh} -- element forwards handlers to kernel configuration
* Eddie Kohler
*
* Copyright (c) 2000-2001 Mazu Networks, 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 "khandlerproxy.hh"
#include <click/error.hh>
#include <click/router.hh>
#include <click/confparse.hh>
#include <click/userutils.hh>
#include <click/llrpc.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
CLICK_DECLS
KernelHandlerProxy::KernelHandlerProxy()
: _detailed_error_message(false)
{
}
KernelHandlerProxy::~KernelHandlerProxy()
{
}
void *
KernelHandlerProxy::cast(const char *n)
{
if (strcmp(n, "HandlerProxy") == 0)
return (HandlerProxy *)this;
else if (strcmp(n, "KernelHandlerProxy") == 0)
return (Element *)this;
else
return 0;
}
int
KernelHandlerProxy::configure(Vector<String> &conf, ErrorHandler *errh)
{
_verbose = false;
return cp_va_parse(conf, this, errh,
cpKeywords,
"VERBOSE", cpBool, "be verbose?", &_verbose,
cpEnd);
}
void
KernelHandlerProxy::add_handlers()
{
add_write_handler("*", star_write_handler, 0);
}
static void
complain_to(ErrorHandler *errh, int errcode, const String &complaint)
{
if (errcode >= 0)
errh->set_error_code(errcode);
errh->verror_text(ErrorHandler::ERR_ERROR, String(), complaint);
}
int
KernelHandlerProxy::complain(ErrorHandler *errh, const String &hname,
int errcode, const String &complaint)
{
if (errh)
complain_to(errh, errcode, complaint);
else {
for (int i = 0; i < _nerr_rcvs; i++)
if ((errh = _err_rcvs[i].hook(hname, _err_rcvs[i].thunk)))
complain_to(errh, errcode, complaint);
}
return -EINVAL;
}
int
KernelHandlerProxy::complain_about_open(ErrorHandler *errh,
const String &hname, int errno_val)
{
const char *dot = find(hname, '.');
String k_elt = hname.substring(hname.begin(), dot);
if (errno_val == ENOENT) {
String try_fn = "/click/" + k_elt;
if (access("/click", F_OK) < 0)
complain(errh, hname, CSERR_NO_ROUTER, "No router installed");
else if (k_elt != "0" && access(try_fn.c_str(), F_OK) < 0)
complain(errh, hname, CSERR_NO_SUCH_ELEMENT, "No element named '" + k_elt.printable() + "'");
else
complain(errh, hname, CSERR_NO_SUCH_HANDLER, "No handler named '" + hname.printable() + "'");
} else if (errno_val == EACCES)
complain(errh, hname, CSERR_PERMISSION, "Permission denied for '" + hname.printable() + "'");
else
complain(errh, hname, CSERR_UNSPECIFIED, "Handler '" + hname.printable() + "' error: " + String(strerror(errno_val)));
return -errno_val;
}
int
KernelHandlerProxy::check_handler_name(const String &hname, ErrorHandler *errh)
{
const char *dot = find(hname, '.');
if (dot == hname.begin() || dot >= hname.end() - 1)
return complain(errh, hname, CSERR_SYNTAX, "Bad handler name '" + hname.printable() + "'");
// check characters for validity -- don't want to screw stuff up
for (const char *s = hname.begin(); s < dot; s++)
if (!isalnum(*s) && *s != '_' && *s != '/' && *s != '@')
return complain(errh, hname, CSERR_SYNTAX, "Bad character in element name '" + hname.substring(hname.begin(), dot).printable() + "'");
for (const char *s = dot + 1; s < hname.end(); s++)
if (*s < 32 || *s >= 127 || *s == '/')
return complain(errh, hname, CSERR_SYNTAX, "Bad character in handler name '" + hname.printable() + "'");
return 0;
}
static String
handler_name_to_file_name(const String &str)
{
if (str[0] == '0' && str[1] == '.')
return "/click/" + str.substring(2);
else {
const char *dot = find(str, '.');
return "/click/" + str.substring(str.begin(), dot) + "/" + str.substring(dot + 1, str.end());
}
}
int
KernelHandlerProxy::star_write_handler(const String &str, Element *e, void *, ErrorHandler *errh)
{
KernelHandlerProxy *khp = static_cast<KernelHandlerProxy *>(e);
if (khp->check_handler_name(str, errh) < 0)
return -1;
khp->set_handler(str, Handler::OP_READ | Handler::OP_WRITE, handler_hook);
return Router::hindex(e, str);
}
int
KernelHandlerProxy::check_handler(const String &hname, bool write, ErrorHandler *errh)
{
if (check_handler_name(hname, errh) < 0)
return 0;
String fn = handler_name_to_file_name(hname);
if (access(fn.c_str(), (write ? W_OK : R_OK)) < 0) {
complain_about_open(errh, hname, errno);
return 0;
}
// If accessible, it still might be a directory rather than a handler.
struct stat buf;
stat(fn.c_str(), &buf);
if (S_ISDIR(buf.st_mode)) {
errh->set_error_code(CSERR_NO_SUCH_HANDLER);
errh->error("No handler named '%#s'", hname.printable().c_str());
return 0;
} else {
errh->message("%s handler '%s' OK", (write ? "Write" : "Read"), hname.printable().c_str());
return 1;
}
}
int
KernelHandlerProxy::handler_hook(int op, String& str, Element* e, const Handler* handler, ErrorHandler* errh)
{
KernelHandlerProxy *khp = static_cast<KernelHandlerProxy *>(e);
const String& hname = handler->name();
String fn = handler_name_to_file_name(hname);
if (op == Handler::OP_READ) {
errno = 0;
str = file_string(fn, 0);
int err = errno;
if (!str && err != 0) {
if (khp->_verbose)
khp->complain_about_open(ErrorHandler::default_handler(), hname, err);
// complain to error receivers
khp->complain_about_open(0, hname, err);
}
return -err;
} else if (op == Handler::OP_WRITE) {
int fd = open(fn.c_str(), O_WRONLY | O_TRUNC);
if (fd < 0)
return khp->complain_about_open(errh, hname, errno);
const char* s = str.begin();
const char* end = str.end();
while (s < end) {
ssize_t written = write(fd, s, end - s);
if (written < 0 && errno != EINTR) {
close(fd);
return khp->complain(errh, hname, CSERR_UNSPECIFIED, fn + ": " + String(strerror(errno)));
} else if (written >= 0)
s += written;
}
if (close(fd) < 0) {
int err = errno;
khp->complain(errh, hname, CSERR_HANDLER_ERROR, "Error executing kernel write handler '" + String(hname) + "'");
if (!khp->_detailed_error_message) {
khp->complain(errh, hname, CSERR_HANDLER_ERROR, "(Check /click/errors for details.)");
khp->_detailed_error_message = true;
}
return -err;
} else
return 0;
} else
return errh->error("odd operation");
}
int
KernelHandlerProxy::llrpc(unsigned command, void *data)
{
if (command == CLICK_LLRPC_PROXY) {
click_llrpc_proxy_st* proxy = static_cast<click_llrpc_proxy_st*>(data);
const Handler* h = static_cast<Handler*>(proxy->proxied_handler);
String fn = handler_name_to_file_name(h->name());
int fd = open(fn.c_str(), O_RDONLY);
int err = errno;
if (fd < 0) {
if (_verbose)
complain_about_open(ErrorHandler::default_handler(), h->name(), err);
// complain to error receivers and return
return complain_about_open(0, h->name(), err);
}
click_chatter("about to %x on %s", proxy->proxied_command, fn.c_str());
int retval = ioctl(fd, proxy->proxied_command, proxy->proxied_data);
err = errno;
close(fd);
return (retval >= 0 ? retval : -err);
} else
return Element::llrpc(command, data);
}
ELEMENT_REQUIRES(userlevel HandlerProxy)
EXPORT_ELEMENT(KernelHandlerProxy)
CLICK_ENDDECLS
syntax highlighted by Code2HTML, v. 0.9.1