/*
 * controlsocket.{cc,hh} -- element listens to TCP/IP or Unix-domain sockets
 * Eddie Kohler
 *
 * Copyright (c) 2000 Massachusetts Institute of Technology
 * Copyright (c) 2001-3 International Computer Science Institute
 * Copyright (c) 2004-2007 Regents of the University of California
 * Copyright (c) 2008 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 "controlsocket.hh"
#include <click/confparse.hh>
#include <click/error.hh>
#include <click/timer.hh>
#include <click/router.hh>
#include <click/straccum.hh>
#include <click/llrpc.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <fcntl.h>
CLICK_DECLS

const char ControlSocket::protocol_version[] = "1.3";

struct ControlSocketErrorHandler : public ErrorHandler { public:

    ControlSocketErrorHandler() {
	_error_code = ControlSocket::CSERR_OK;
    }

    const Vector<String> &messages() const {
	return _messages;
    }
    int error_code() const {
	return _error_code;
    }

    void *emit(const String &str, void *user_data, bool more);

  private:

    Vector<String> _messages;
    int _error_code;

};

void *
ControlSocketErrorHandler::emit(const String &str, void *, bool)
{
    String landmark;
    const char *s = parse_anno(str, str.begin(), str.end(), "l", &landmark,
			       "#cserr", &_error_code, (const char *) 0);
    _messages.push_back(clean_landmark(landmark, true)
			+ str.substring(s, str.end()));
    return 0;
}


ControlSocket::ControlSocket()
  : _socket_fd(-1), _proxy(0), _full_proxy(0), _retry_timer(0)
{
}

ControlSocket::~ControlSocket()
{
}

int
ControlSocket::configure(Vector<String> &conf, ErrorHandler *errh)
{
  String socktype;
  if (cp_va_kparse(conf, this, errh,
		   "TYPE", cpkP+cpkM, cpString, &socktype,
		   cpIgnoreRest, cpEnd) < 0)
    return -1;

  // remove keyword arguments
  bool read_only = false, verbose = false, retry_warnings = true, localhost = false;
  _retries = 0;
  if (cp_va_kparse_remove_keywords(conf, this, errh,
		"READONLY", 0, cpBool, &read_only,
		"PROXY", 0, cpElement, &_proxy,
		"VERBOSE", 0, cpBool, &verbose,
		"RETRIES", 0, cpInteger, &_retries,
		"RETRY_WARNINGS", 0, cpBool, &retry_warnings,
		"LOCALHOST", 0, cpBool, &localhost,
		cpEnd) < 0)
    return -1;
  _read_only = read_only;
  _verbose = verbose;
  _retry_warnings = retry_warnings;
  _localhost = localhost;

  socktype = socktype.upper();
  if (socktype == "TCP") {
    _tcp_socket = true;
    if (cp_va_kparse(conf, this, errh,
		     "TYPE", cpkP+cpkM, cpIgnore,
		     "PORT", cpkP+cpkM, cpWord, &_unix_pathname, cpEnd) < 0)
      return -1;
    uint16_t portno;
    int portno_int;
    if (cp_tcpudp_port(_unix_pathname, IP_PROTO_TCP, &portno, this))
	_unix_pathname = String(portno);
    else if (_unix_pathname && _unix_pathname.back() == '+'
	     && cp_integer(_unix_pathname.substring(0, -1), 0, &portno_int)
	     && portno_int > 0 && portno_int < 65536)
	_unix_pathname = String(portno_int) + "+";
    else
	return errh->error("PORT requires TCP port");

  } else if (socktype == "UNIX") {
    _tcp_socket = false;
    if (cp_va_kparse(conf, this, errh,
		     "TYPE", cpkP+cpkM, cpIgnore,
		     "FILENAME", cpkP+cpkM, cpFilename, &_unix_pathname, cpEnd) < 0)
      return -1;
    if (_unix_pathname.length() >= (int)sizeof(((struct sockaddr_un *)0)->sun_path))
      return errh->error("filename too long");

  } else
    return errh->error("unknown socket type '%s'", socktype.c_str());

  return 0;
}


int
ControlSocket::initialize_socket_error(ErrorHandler *errh, const char *syscall)
{
  int e = errno;		// preserve errno

  if (_socket_fd >= 0) {
    close(_socket_fd);
    _socket_fd = -1;
  }

  if (_retries >= 0) {
    if (_retry_warnings)
      errh->warning("%s: %s (%d %s left)", syscall, strerror(e), _retries + 1, (_retries == 0 ? "try" : "tries"));
    return -EINVAL;
  } else
    return errh->error("%s: %s", syscall, strerror(e));
}

int
ControlSocket::initialize_socket(ErrorHandler *errh)
{
  _retries--;

  // open socket, set options, bind to address
  if (_tcp_socket) {
    _socket_fd = socket(PF_INET, SOCK_STREAM, 0);
    if (_socket_fd < 0)
      return initialize_socket_error(errh, "socket");
    int sockopt = 1;
    if (setsockopt(_socket_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&sockopt, sizeof(sockopt)) < 0)
      errh->warning("setsockopt: %s", strerror(errno));

    // bind to port
    int portno;
    (void) cp_integer(_unix_pathname.begin(), _unix_pathname.end(), 0, &portno);
    struct sockaddr_in sa;
    sa.sin_family = AF_INET;
    sa.sin_port = htons(portno);
    if (_localhost)
	sa.sin_addr.s_addr = INADDR_LOOPBACK;
    else
	sa.sin_addr.s_addr = INADDR_ANY;
    int tries = 0;
    while (bind(_socket_fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
	if (tries > 10 || _unix_pathname.back() != '+' || portno >= 65534
	    || (_retries >= 0 && hotswap_element()))
	    return initialize_socket_error(errh, "bind");
	++portno, ++tries;
	sa.sin_port = htons(portno);
    }
    _unix_pathname = String(portno);

  } else {
    _socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
    if (_socket_fd < 0)
      return initialize_socket_error(errh, "socket");

    // bind to port
    struct sockaddr_un sa;
    sa.sun_family = AF_UNIX;
    memcpy(sa.sun_path, _unix_pathname.c_str(), _unix_pathname.length() + 1);
    if (bind(_socket_fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
      return initialize_socket_error(errh, "bind");
  }

  // start listening
  if (listen(_socket_fd, 2) < 0)
    return initialize_socket_error(errh, "listen");

  // nonblocking I/O and close-on-exec for the socket
  fcntl(_socket_fd, F_SETFL, O_NONBLOCK);
  fcntl(_socket_fd, F_SETFD, FD_CLOEXEC);

  add_select(_socket_fd, SELECT_READ);
  return 0;
}

void
ControlSocket::retry_hook(Timer *t, void *thunk)
{
  ControlSocket *cs = (ControlSocket *)thunk;
  if (cs->_socket_fd >= 0)
    /* nada */;
  else if (cs->initialize_socket(ErrorHandler::default_handler()) >= 0)
    /* nada */;
  else if (cs->_retries >= 0)
    t->reschedule_after_sec(1);
  else
    cs->router()->please_stop_driver();
}

int
ControlSocket::initialize(ErrorHandler *errh)
{
  // check for a full proxy
  if (_proxy)
    _full_proxy = static_cast<HandlerProxy *>(_proxy->cast("HandlerProxy"));

  // ask the proxy to send us errors
  if (_full_proxy)
    _full_proxy->add_error_receiver(proxy_error_function, this);

  if (initialize_socket(errh) >= 0)
    return 0;
  else if (_retries >= 0) {
    _retry_timer = new Timer(retry_hook, this);
    _retry_timer->initialize(this);
    _retry_timer->schedule_after_sec(1);
    return 0;
  } else
    return -1;
}

Element *
ControlSocket::hotswap_element() const
{
    if (Element *e = Element::hotswap_element())
	if (ControlSocket *cs = (ControlSocket *)e->cast("ControlSocket"))
	    if (cs->_tcp_socket == _tcp_socket
		&& (cs->_unix_pathname == _unix_pathname
		    || (_tcp_socket && _unix_pathname.back() == '+')))
		return cs;
    return 0;
}

void
ControlSocket::take_state(Element *e, ErrorHandler *errh)
{
  ControlSocket *cs = (ControlSocket *) e->cast("ControlSocket");

  if (_socket_fd >= 0) {
    errh->error("already initialized, can't take state");
    return;
  }

  _socket_fd = cs->_socket_fd;
  _unix_pathname = cs->_unix_pathname; // in case _unix_pathname == "41930+"
  cs->_socket_fd = -1;
  _in_texts.swap(cs->_in_texts);
  _out_texts.swap(cs->_out_texts);
  _flags.swap(cs->_flags);

  if (_socket_fd >= 0)
    add_select(_socket_fd, SELECT_READ);
  for (int i = 0; i < _flags.size(); i++)
    if (_flags[i] >= 0)
      add_select(i, SELECT_READ | SELECT_WRITE);
}

void
ControlSocket::cleanup(CleanupStage)
{
  if (_full_proxy)
    _full_proxy->remove_error_receiver(proxy_error_function, this);
  if (_socket_fd >= 0) {
    // shut down the listening socket in case we forked
#ifdef SHUT_RDWR
    shutdown(_socket_fd, SHUT_RDWR);
#else
    shutdown(_socket_fd, 2);
#endif
    close(_socket_fd);
    if (!_tcp_socket)
      unlink(_unix_pathname.c_str());
    _socket_fd = -1;
  }
  for (int i = 0; i < _flags.size(); i++)
    if (_flags[i] >= 0) {
      flush_write(i, false);	// try one last time to emit all data
      close(i);
      _flags[i] = -1;
    }
  if (_retry_timer) {
    delete _retry_timer;
    _retry_timer = 0;
  }
}

int
ControlSocket::message(int fd, int code, const String &s, bool continuation)
{
  assert(code >= 100 && code <= 999);
  if (fd >= 0 && !(_flags[fd] & WRITE_CLOSED))
    _out_texts[fd] += String(code) + (continuation ? "-" : " ") + s.printable() + "\r\n";
  return ANY_ERR;
}

int
ControlSocket::transfer_messages(int fd, int default_code, const String &msg,
				 ControlSocketErrorHandler *errh)
{
  int code = errh->error_code();
  if (code == CSERR_OK)
    code = default_code;
  const Vector<String> &messages = errh->messages();

  if (msg) {
    if (messages.size() > 0)
      message(fd, code, msg + ":", true);
    else
      message(fd, code, msg, false);
  }

  for (int i = 0; i < messages.size(); i++)
    message(fd, code, messages[i], i < messages.size() - 1);

  return ANY_ERR;
}

static String
canonical_handler_name(const String &n)
{
  const char *dot = find(n, '.');
  if (dot == n.begin() || (dot == n.begin() + 1 && n.front() == '0'))
    return n.substring(dot + 1, n.end());
  else
    return n;
}

String
ControlSocket::proxied_handler_name(const String &n) const
{
  if (_full_proxy && find(n, '.') == n.end())
    return "." + n;
  else
    return n;
}

const Handler*
ControlSocket::parse_handler(int fd, const String &full_name, Element **es)
{
  // Parse full_name into element_name and handler_name.
  String canonical_name = canonical_handler_name(full_name);

  // Check for proxy.
  if (_proxy) {
    // collect errors from proxy
    ControlSocketErrorHandler errh;
    _proxied_handler = proxied_handler_name(canonical_name);
    _proxied_errh = &errh;
    const Handler* h = Router::handler(_proxy, _proxied_handler);
    _proxied_errh = 0;

    if (errh.nerrors() > 0) {
      transfer_messages(fd, CSERR_NO_SUCH_HANDLER, String(), &errh);
      return 0;
    } else if (!h) {
      message(fd, CSERR_NO_SUCH_HANDLER, "No proxied handler named '" + full_name + "'");
      return 0;
    } else {
      *es = _proxy;
      return h;
    }
  }

  // Otherwise, find element.
  Element *e;
  const char *dot = find(canonical_name, '.');
  String hname;

  if (dot != canonical_name.end()) {
    String ename = canonical_name.substring(canonical_name.begin(), dot);
    e = router()->find(ename);
    if (!e) {
      int num;
      if (cp_integer(ename, &num) && num > 0 && num <= router()->nelements())
	e = router()->element(num - 1);
    }
    if (!e) {
      message(fd, CSERR_NO_SUCH_ELEMENT, "No element named '" + ename + "'");
      return 0;
    }
    hname = canonical_name.substring(dot + 1, canonical_name.end());
  } else {
    e = router()->root_element();
    hname = canonical_name;
  }

  // Then find handler.
  const Handler* h = Router::handler(e, hname);
  if (h && h->visible()) {
    *es = e;
    return h;
  } else {
    message(fd, CSERR_NO_SUCH_HANDLER, "No handler named '" + full_name + "'");
    return 0;
  }
}

int
ControlSocket::read_command(int fd, const String &handlername, String param)
{
  Element *e;
  const Handler* h = parse_handler(fd, handlername, &e);
  if (!h)
    return ANY_ERR;
  else if (!h->read_visible())
    return message(fd, CSERR_PERMISSION, "Handler '" + handlername + "' write-only");

  // collect errors from proxy
  ControlSocketErrorHandler errh;
  _proxied_handler = h->name();
  _proxied_errh = &errh;
  String data = h->call_read(e, param, &errh);
  _proxied_errh = 0;

  // did we get an error message?
  if (errh.nerrors() > 0)
    return transfer_messages(fd, CSERR_UNSPECIFIED, "Read handler '" + handlername + "' error", &errh);

  message(fd, CSERR_OK, "Read handler '" + handlername + "' OK");
  _out_texts[fd] += "DATA " + String(data.length()) + "\r\n";
  _out_texts[fd] += data;
  return 0;
}

int
ControlSocket::write_command(int fd, const String &handlername, String data)
{
  Element *e;
  const Handler* h = parse_handler(fd, handlername, &e);
  if (!h)
    return ANY_ERR;
  else if (!h->writable())
    return message(fd, CSERR_PERMISSION, "Handler '" + handlername + "' read-only");

  if (_read_only)
    return message(fd, CSERR_PERMISSION, "Permission denied for '" + handlername + "'");

#ifdef LARGEST_HANDLER_WRITE
  if (data.length() > LARGEST_HANDLER_WRITE)
    return message(fd, CSERR_DATA_TOO_BIG, "Data too large for write handler '" + handlername + "'");
#endif

  ControlSocketErrorHandler errh;

  // call handler
  int result = h->call_write(data, e, &errh);

  // add a generic error message for certain handler codes
  int code = errh.error_code();
  if (code == CSERR_OK) {
    if (errh.nerrors() > 0 || result < 0)
      code = CSERR_HANDLER_ERROR;
    else if (errh.nwarnings() > 0)
      code = CSERR_OK_HANDLER_WARNING;
  }

  String msg;
  if (code == CSERR_OK)
    msg = "Write handler '" + handlername + "' OK";
  else if (code == CSERR_OK_HANDLER_WARNING)
    msg = "Write handler '" + handlername + "' OK with warnings";
  else if (code == CSERR_HANDLER_ERROR)
    msg = "Write handler '" + handlername + "' error";
  transfer_messages(fd, code, msg, &errh);
  return 0;
}

int
ControlSocket::check_command(int fd, const String &hname, bool write)
{
  int ok = 0;
  int any_visible = 0;
  ControlSocketErrorHandler errh;

  if (_full_proxy) {
    String phname = canonical_handler_name(hname);
    if (find(phname, '.') == phname.end())
      phname = "." + phname;
    ok = _full_proxy->check_handler(phname, write, &errh);
  } else {
    Element *e;
    const Handler* h = parse_handler(fd, hname, &e);
    if (!h)
      return 0;			// error messages already reported
    ok = (h->visible() && (write ? h->write_visible() : h->read_visible()));
    any_visible = h->visible();
  }

  // remember _read_only!
  if (write && _read_only && ok)
    return message(fd, CSERR_PERMISSION, "Permission denied for '" + hname + "'");
  else if (errh.messages().size() > 0)
    transfer_messages(fd, CSERR_OK, String(), &errh);
  else if (ok)
    message(fd, CSERR_OK, String(write ? "Write" : "Read") + " handler '" + hname + "' OK");
  else if (any_visible)
    message(fd, CSERR_NO_SUCH_HANDLER, "Handler '" + hname + (write ? "' not writable" : "' not readable"));
  else
    message(fd, CSERR_NO_SUCH_HANDLER, "No " + String(write ? "write" : "read") + " handler named '" + hname + "'");
  return 0;
}

int
ControlSocket::llrpc_command(int fd, const String &llrpcname, String data)
{
  const char *octothorp = find(llrpcname, '#');
  uint32_t command;
  if (!cp_integer(llrpcname.substring(octothorp + 1, llrpcname.end()), 16, &command))
    return message(fd, CSERR_SYNTAX, "Syntax error in LLRPC name '" + llrpcname + "'");
  // transform net LLRPC id into host LLRPC id
  command = CLICK_LLRPC_NTOH(command);

  Element *e;
  const Handler* h = parse_handler(fd, llrpcname.substring(llrpcname.begin(), octothorp) + ".name", &e);
  if (!h)
    return ANY_ERR;

  int size = _CLICK_IOC_SIZE(command);
  if (!size || !(command & (_CLICK_IOC_IN | _CLICK_IOC_OUT)) || !(command & _CLICK_IOC_FLAT))
    return message(fd, CSERR_UNIMPLEMENTED, "Cannot call LLRPC '" + llrpcname + "' remotely");

  if (_read_only)		// can't tell whether an LLRPC is read-only;
				// so disallow them all
    return message(fd, CSERR_PERMISSION, "Permission denied for '" + llrpcname + "'");

  if ((command & _CLICK_IOC_IN) && data.length() != size)
    return message(fd, CSERR_LLRPC_ERROR, "LLRPC '" + llrpcname + "' requires " + String(size) + " bytes input data");
  else if (!(command & _CLICK_IOC_IN))
    data = String::make_garbage(size);

  // collect errors from proxy
  ControlSocketErrorHandler errh;
  _proxied_handler = llrpcname;
  _proxied_errh = &errh;

  int retval;
  if (_proxy) {
    struct click_llrpc_proxy_st pst;
    pst.proxied_handler = (void*) h;
    pst.proxied_command = command;
    pst.proxied_data = data.mutable_data();
    retval = _proxy->llrpc(CLICK_LLRPC_PROXY, &pst);
  } else
    retval = e->llrpc(command, data.mutable_data());

  _proxied_errh = 0;

  // did we get an error message?
  String msg;
  if (retval < 0)
    msg = "LLRPC '" + llrpcname + "' error: " + String(strerror(-retval));
  else if (errh.nerrors() > 0)
    msg = "LLRPC '" + llrpcname + "' error";
  else
    msg = "LLRPC '" + llrpcname + "' OK";
  int code = (retval < 0 || errh.nerrors() > 0 ? CSERR_LLRPC_ERROR : CSERR_OK);
  transfer_messages(fd, code, msg, &errh);

  if (code == CSERR_OK) {
    if (!(command & _CLICK_IOC_OUT))
      data = String();
    _out_texts[fd] += "DATA " + String(data.length()) + "\r\n";
    _out_texts[fd] += data;
  }
  return 0;
}

int
ControlSocket::parse_command(int fd, const String &line)
{
  // split 'line' into words; don't use cp_ functions since they strip comments
  Vector<String> words;
  const char *data = line.data();
  int len = line.length();
  for (int pos = 0; pos < len; ) {
      while (pos < len && isspace((unsigned char) data[pos]))
      pos++;
    int first = pos;
    while (pos < len && !isspace((unsigned char) data[pos]))
      pos++;
    if (first < pos)
      words.push_back(line.substring(first, pos - first));
  }
  if (words.size() == 0)
    return 0;

  // branch on command
  String command = words[0].upper();
  if (command == "READ" || command == "GET" || command == "WRITE"
      || command == "SET") {
      if (words.size() < 2)
	  return message(fd, CSERR_SYNTAX, "Wrong number of arguments");
      String data;
      if (words.size() > 2)
	  data = line.substring(words[2].begin(), words.back().end());
      if (command[0] == 'R' || command[0] == 'G')
	  return read_command(fd, words[1], data);
      else
	  return write_command(fd, words[1], data);

  } else if (command == "READDATA" || command == "WRITEDATA"
	     || command == "SETDATA") {
      if (words.size() != 3)
	  return message(fd, CSERR_SYNTAX, "Wrong number of arguments");
      int datalen;
      if (!cp_integer(words[2], &datalen) || datalen < 0)
	  return message(fd, CSERR_SYNTAX, "Syntax error in '%s'", command.c_str());
      if (_in_texts[fd].length() < datalen) {
	  if (_flags[fd] & READ_CLOSED)
	      return message(fd, CSERR_SYNTAX, "Not enough data");
	  else			// retry
	      return 1;
      }
      String data = _in_texts[fd].substring(0, datalen);
      _in_texts[fd] = _in_texts[fd].substring(datalen);
      if (command[0] == 'R')
	  return read_command(fd, words[1], data);
      else
	  return write_command(fd, words[1], data);

  } else if (command == "READUNTIL" || command == "WRITEUNTIL") {
      if (words.size() < 2)
	  return message(fd, CSERR_SYNTAX, "Wrong number of arguments");
      String until;
      if (words.size() > 2)
	  until = line.substring(words[2].begin(), words.back().end());
      const char *s = _in_texts[fd].begin(), *linebegin, *lineend;
      while (1) {
	  linebegin = lineend = s;
	  for (; s != _in_texts[fd].end() && *s != '\n' && *s != '\r'; ++s)
	      if (!isspace((unsigned char) *s))
		  lineend = s + 1;
	  if (s == _in_texts[fd].end()) {
	      if (_flags[fd] & READ_CLOSED)
		  return message(fd, CSERR_SYNTAX, "Connection closed");
	      else		// retry
		  return 1;
	  }
	  if (*s == '\r' && s + 1 != _in_texts[fd].end() && s[1] == '\n')
	      s += 2;
	  else
	      ++s;
	  if (_in_texts[fd].substring(linebegin, lineend) == until)
	      break;
      }
      String data = _in_texts[fd].substring(_in_texts[fd].begin(), linebegin);
      _in_texts[fd] = _in_texts[fd].substring(lineend, _in_texts[fd].end());
      if (command[0] == 'R')
	  return read_command(fd, words[1], data);
      else
	  return write_command(fd, words[1], data);

  } else if (command == "CHECKREAD" || command == "CHECKWRITE") {
    if (words.size() != 2)
      return message(fd, CSERR_SYNTAX, "Wrong number of arguments");
    return check_command(fd, words[1], command[5] == 'W');

  } else if (command == "LLRPC") {
    if (words.size() != 2 && words.size() != 3)
      return message(fd, CSERR_SYNTAX, "Wrong number of arguments");
    int datalen = 0;
    if (words.size() == 3 && (!cp_integer(words[2], &datalen) || datalen < 0))
      return message(fd, CSERR_SYNTAX, "Syntax error in 'llrpc'");
    if (_in_texts[fd].length() < datalen) {
      if (_flags[fd] & READ_CLOSED)
	return message(fd, CSERR_SYNTAX, "Not enough data");
      else			// retry
	return 1;
    }
    String data = _in_texts[fd].substring(0, datalen);
    _in_texts[fd] = _in_texts[fd].substring(datalen);
    return llrpc_command(fd, words[1], data);

  } else if (command == "CLOSE" || command == "QUIT") {
    if (words.size() != 1)
      message(fd, CSERR_SYNTAX, "Bad command syntax");
    message(fd, CSERR_OK, "Goodbye!");
    _flags[fd] |= READ_CLOSED;
    _in_texts[fd] = String();
    return 0;

  } else if (command == "HELP") {
    message(fd, CSERR_OK, "Commands supported:", true);
    message(fd, CSERR_OK, "READ handler [arg...]   call read handler, return DATA", true);
    message(fd, CSERR_OK, "READDATA handler len    call read handler with len data bytes, return DATA", true);
    message(fd, CSERR_OK, "READUNTIL handler term  call read handler, take data until term, return DATA", true);
    message(fd, CSERR_OK, "WRITE handler [arg...]  call write handler", true);
    message(fd, CSERR_OK, "WRITEDATA handler len   call write handler, pass len data bytes", true);
    message(fd, CSERR_OK, "WRITEUNTIL handler term call write handler, take data until term", true);
    message(fd, CSERR_OK, "CHECKREAD handler       check if read handler is valid", true);
    message(fd, CSERR_OK, "CHECKWRITE handler      check if write handler is valid", true);
    message(fd, CSERR_OK, "LLRPC elt#number [len]  call LLRPC, pass len data bytes, return DATA", true);
    message(fd, CSERR_OK, "QUIT                    close connection");
    return 0;

  } else
    return message(fd, CSERR_UNIMPLEMENTED, "Command '" + command + "' unimplemented");
}

void
ControlSocket::flush_write(int fd, bool read_needs_processing)
{
    assert(_flags[fd] >= 0);
    if (!(_flags[fd] & WRITE_CLOSED)) {
	int w = 0;
	while (_out_texts[fd].length()) {
	    const char *x = _out_texts[fd].data();
	    w = write(fd, x, _out_texts[fd].length());
	    if (w < 0 && errno != EINTR)
		break;
	    if (w > 0)
		_out_texts[fd] = _out_texts[fd].substring(w);
	}
	if (w < 0 && errno == EPIPE)
	    _flags[fd] |= WRITE_CLOSED;
	// don't select writes unless we have data to write (or read needs more
	// processing)
	if (_out_texts[fd].length() || read_needs_processing)
	    add_select(fd, SELECT_WRITE);
	else
	    remove_select(fd, SELECT_WRITE);
    }
}

void
ControlSocket::selected(int fd, int)
{
    if (fd == _socket_fd) {
	union { struct sockaddr_in in; struct sockaddr_un un; } sa;
#if HAVE_ACCEPT_SOCKLEN_T
	socklen_t sa_len;
#else
	int sa_len;
#endif
	sa_len = sizeof(sa);
	int new_fd = accept(_socket_fd, (struct sockaddr *)&sa, &sa_len);

	if (new_fd < 0) {
	    if (errno != EAGAIN)
		click_chatter("%s: accept: %s", declaration().c_str(), strerror(errno));
	    return;
	}

	if (_verbose) {
	    if (_tcp_socket)
		click_chatter("%s: opened connection %d from %s.%d", declaration().c_str(), new_fd, IPAddress(sa.in.sin_addr).unparse().c_str(), ntohs(sa.in.sin_port));
	    else
		click_chatter("%s: opened connection %d", declaration().c_str(), new_fd);
	}

	fcntl(new_fd, F_SETFL, O_NONBLOCK);
	fcntl(new_fd, F_SETFD, FD_CLOEXEC);
	add_select(new_fd, SELECT_READ | SELECT_WRITE);

	while (new_fd >= _in_texts.size()) {
	    _in_texts.push_back(String());
	    _out_texts.push_back(String());
	    _flags.push_back(-1);
	}
	_in_texts[new_fd] = String();
	_out_texts[new_fd] = String();
	_flags[new_fd] = 0;

	fd = new_fd;
	_out_texts[new_fd] = "Click::ControlSocket/" + String(protocol_version) + "\r\n";
    }

    // find file descriptor
    if (fd >= _in_texts.size() || _flags[fd] < 0)
	return;

    // read commands from socket (but only a bit on each select)
    if (!(_flags[fd] & READ_CLOSED)) {
	char buf[2048];
	int r = read(fd, buf, 2048);
	if (r > 0)
	    _in_texts[fd].append(buf, r);
	else if (r == 0 || (r < 0 && errno != EAGAIN && errno != EINTR))
	    _flags[fd] |= READ_CLOSED;
    }

    // parse commands
    // 16.Jun.2004: process only one command each time through
    bool blocked = false;
    if (_in_texts[fd].length()) {
	const char *in_text = _in_texts[fd].data();
	int len = _in_texts[fd].length();
	int pos = 0;
	while (pos < len && in_text[pos] != '\r' && in_text[pos] != '\n')
	    pos++;
	if (pos < len || (_flags[fd] & READ_CLOSED)) {
	    // have a complete command, parse it

	    // include end of line
	    if (pos < len - 1 && in_text[pos] == '\r' && in_text[pos+1] == '\n')
		pos += 2;
	    else if (pos < len)	// '\r' or '\n' alone
		pos++;

	    // grab string
	    String old_text = _in_texts[fd];
	    String line = old_text.substring(0, pos);
	    _in_texts[fd] = old_text.substring(pos);

	    // parse each individual command
	    if (parse_command(fd, line) > 0) {
		// more data to come, so wait
		_in_texts[fd] = old_text;
		blocked = true;
	    }
	} else
	    // 12.Jul.2006, Cliff Frey: write incomplete, so we are blocked
	    blocked = true;
    }

    // write data until blocked
    // The 2nd argument causes write events to remain selected when commands
    // remain to be processed (whether or not CS has data to write).
    flush_write(fd, _in_texts[fd].length() && !blocked);

    // maybe close out
    if (((_flags[fd] & READ_CLOSED) && !_in_texts[fd].length() && !_out_texts[fd].length())
	|| (_flags[fd] & WRITE_CLOSED)) {
	remove_select(fd, SELECT_READ | SELECT_WRITE);
	close(fd);
	if (_verbose)
	    click_chatter("%s: closed connection %d", declaration().c_str(), fd);
	_flags[fd] = -1;
    }
}

ErrorHandler *
ControlSocket::proxy_error_function(const String &h, void *thunk)
{
  ControlSocket *cs = static_cast<ControlSocket *>(thunk);
  return (h == cs->_proxied_handler ? cs->_proxied_errh : 0);
}

void
ControlSocket::add_handlers()
{
    add_data_handlers(_tcp_socket ? "port" : "filename", Handler::OP_READ, &_unix_pathname);
}

CLICK_ENDDECLS
ELEMENT_REQUIRES(userlevel)
EXPORT_ELEMENT(ControlSocket)
