📄 csclient.cc
字号:
/* * csclient.cc * Douglas S. J. De Couto */#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <unistd.h>#include <string.h>#include <errno.h>#include <assert.h>#include <stdio.h>#include <ctype.h>#include <algorithm>#include "csclient.hh"#define INCLUDE_TEST_CODE 1#if INCLUDE_TEST_CODE#include <iostream>#endif#define check_init() do { if (!_init) return init_err; } while (false);ControlSocketClient::err_tControlSocketClient::configure(unsigned int host_ip, unsigned short port){ if (_init) return reinit_err; _host = host_ip; _port = port; _fd = socket(PF_INET, SOCK_STREAM, 0); if (_fd < 0) return sys_err; /* * connect to remote ControlSocket */ struct sockaddr_in sa; memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_addr.s_addr = _host; sa.sin_port = htons(port); char namebuf[32]; snprintf(namebuf, 32, "%u.%u.%u.%u:%hu", (_host & 0xff) >> 0, (_host & 0xff00) >> 8, (_host & 0xff0000) >> 16, (_host & 0xff000000) >> 24, port); _name = namebuf; int res = connect(_fd, (struct sockaddr *) &sa, sizeof(sa)); if (res < 0) { int save_errno = errno; ::close(_fd); errno = save_errno; return sys_err; } int major, minor; unsigned int slash, dot; /* * check that we get the expected banner */ string buf; err_t err = readline(buf); if (err != no_err) { int save_errno = errno; ::close(_fd); errno = save_errno; return err; } slash = buf.find('/'); dot = (slash != string::npos ? buf.find('.', slash + 1) : string::npos); if (slash == string::npos || dot == string::npos) { ::close(_fd); return click_err; /* bad format */ } /* * check ControlSocket protocol version */ major = atoi(buf.substr(slash + 1, dot - slash - 1).c_str()); minor = atoi(buf.substr(dot + 1, buf.size() - dot - 1).c_str()); if (major != PROTOCOL_MAJOR_VERSION || minor < PROTOCOL_MINOR_VERSION) { ::close(_fd); return click_err; /* wrong version */ } _init = true; return no_err;}ControlSocketClient::err_tControlSocketClient::close(){ check_init(); _init = false; int res = ::close(_fd); if (res < 1) return sys_err; else return no_err;}ControlSocketClient::err_tControlSocketClient::readline(string &buf){ assert(_fd);#define MAX_LINE_SZ 1024 /* arbitrary... to prevent weirdness */ /* * keep calling read() to get one character at a time, until we get * a line. not very ``efficient'', but who cares? */ char c = 0; buf.resize(0); do { int res = ::read(_fd, (void *) &c, 1); if (res < 0) return sys_err; if (res != 1) return sys_err; buf += c; if (buf.size() > MAX_LINE_SZ) return click_err; } while (c != '\n'); return no_err;}intControlSocketClient::get_resp_code(string line){ if (line.size() < 3) return -1; return atoi(line.substr(0, 3).c_str());}intControlSocketClient::get_data_len(string line){ unsigned int i; for (i = 0; i < line.size() && !isdigit(line[i]); i++) ; // scan string if (i == line.size()) return -1; return atoi(line.substr(i, line.size() - i).c_str());}ControlSocketClient::err_tControlSocketClient::read(string el, string handler, string &response){ check_init(); if (el.size() > 0) handler = el + "." + handler; string cmd = "READ " + handler + "\n"; int res = ::write(_fd, cmd.c_str(), cmd.size()); if (res < 0) return sys_err; if ((size_t) res != cmd.size()) return sys_err; string cmd_resp; string line; do { err_t err = readline(line); if (err != no_err) return err; if (line.size() < 4) return click_err; cmd_resp += line; } while (line[3] == '-'); int code = get_resp_code(line); if (code != CODE_OK && code != CODE_OK_WARN) return handle_err_code(code); res = readline(line); if (res < 0) return click_err; int num = get_data_len(line); if (num < 0) return click_err; response.resize(0); if (num == 0) return no_err; char *buf = new char[num]; int num_read = 0; while (num_read < num) { res = ::read(_fd, buf + num_read, num - num_read); if (res < 0) { delete[] buf; return sys_err; } num_read += res; } response.append(buf, num); delete[] buf; return no_err;}ControlSocketClient::err_tControlSocketClient::read(string el, string handler, char *buf, int &bufsz){ string resp; err_t err = read(el, handler, resp); if (err != no_err) return err; bufsz = min((size_t) bufsz, resp.size()); memcpy(buf, resp.c_str(), bufsz); if (resp.size() > (size_t) bufsz) return too_short; else return no_err;}ControlSocketClient::err_tControlSocketClient::write(string el, string handler, const char *buf, int bufsz){ check_init(); if (el.size() > 0) handler = el + "." + handler; char cbuf[10]; snprintf(cbuf, sizeof(cbuf), "%d", bufsz); string cmd = "WRITEDATA " + handler + " " + cbuf + "\n"; int res = ::write(_fd, cmd.c_str(), cmd.size()); if (res < 0) return sys_err; if ((size_t) res != cmd.size()) return sys_err; res = ::write(_fd, buf, bufsz); if (res < 0) return sys_err; if (res != bufsz) return sys_err; string cmd_resp; string line; do { err_t err = readline(line); if (err != no_err) return err; if (line.size() < 4) return click_err; cmd_resp += line; } while (line[3] == '-'); int code = get_resp_code(line); if (code != CODE_OK && code != CODE_OK_WARN) { cout << "CCCC " << code << endl; return handle_err_code(code); } return no_err;}ControlSocketClient::err_tControlSocketClient::write(string el, string handler, string data){ return write(el, handler, data.c_str(), data.size());}ControlSocketClient::err_tControlSocketClient::handle_err_code(int code){ switch (code) { case CODE_SYNTAX_ERR: return click_err; break; case CODE_UNIMPLEMENTED: return click_err; break; case CODE_NO_ELEMENT: return no_element; break; case CODE_NO_HANDLER: return no_handler; break; case CODE_HANDLER_ERR: return handler_err; break; case CODE_PERMISSION: return handler_no_perm; break; default: return click_err; break; } return click_err;}vector<string> ControlSocketClient::split(string s, size_t offset, char terminator){ vector<string> v; size_t pos = offset; size_t len = s.size(); while (pos < len) { size_t start = pos; while (pos < len && s[pos] != terminator) pos++; if (start < pos || pos < len) v.push_back(s.substr(start, pos - start)); pos++; } return v;}ControlSocketClient::err_t ControlSocketClient::get_config_el_names(vector<string> &els){ string resp; err_t err = read("", "list", resp); if (err != no_err) return err; /* parse how many els */ int i = resp.find('\n'); int num = atoi(resp.substr(0, i).c_str()); els = split(resp, i + 1, '\n'); if (els.size() != (size_t) num) return handler_bad_format; return no_err; }ControlSocketClient::err_tControlSocketClient::get_string_vec(string el, string h, vector<string> &v){ string resp; err_t err = read(el, h, resp); if (err != no_err) return err; v = split(resp, 0, '\n'); return no_err;}ControlSocketClient::err_t ControlSocketClient::get_el_handlers(string el, vector<handler_info_t> &handlers){ vector<handler_info_t> v; vector<string> vh; string buf; err_t err = read(el, "handlers", buf); if (err != no_err) return err; vh = split(buf, 0, '\n'); for (vector<string>::iterator i = vh.begin(); i != vh.end(); i++) { string &s = *i; size_t j; for (j = 0; j < s.size() && !isspace(s[j]); j++) ; /* find record split -- don't use s.find because could be any whitespace */ if (j == s.size()) return click_err; handler_info_t hi; hi.element_name = el; hi.handler_name = trim(s.substr(0, j)); while (j < s.size() && isspace(s[j])) j++; for ( ; j < s.size(); j++) { if (tolower(s[j] == 'r')) hi.can_read = true; else if (tolower(s[j] == 'w')) hi.can_write = true; else if (isspace(s[j])) break; } v.push_back(hi); } handlers = v; return no_err;}ControlSocketClient::err_t ControlSocketClient::check_handler(string el, string h, bool is_write, bool &exists){ check_init(); if (el.size() > 0) h = el + "." + h; string cmd = (is_write ? "CHECKWRITE " : "CHECKREAD ") + h + "\n"; int res = ::write(_fd, cmd.c_str(), cmd.size()); if (res < 0) return sys_err; if ((size_t) res != cmd.size()) return sys_err; string cmd_resp; string line; do { err_t err = readline(line); if (err != no_err) return err; if (line.size() < 4) return click_err; cmd_resp += line; } while (line[3] == '-'); int code = get_resp_code(line); switch (code) { case CODE_OK: case CODE_OK_WARN: exists = true; return no_err;; case CODE_NO_ELEMENT: case CODE_NO_HANDLER: case CODE_HANDLER_ERR: case CODE_PERMISSION: exists = false; return no_err; case CODE_UNIMPLEMENTED: if (el.size() == 0) return handle_err_code(code); /* no workaround for top-level router handlers */ else return check_handler_workaround(el, h, is_write, exists); default: return handle_err_code(code); }}ControlSocketClient::err_t ControlSocketClient::check_handler_workaround(string el, string h, bool is_write, bool &exists){ /* * If talking to an old ControlSocket, try the "handlers" handler * instead. */ vector<handler_info_t> v; err_t err = get_el_handlers(el, v); if (err != no_err) return err; for (vector<handler_info_t>::iterator i = v.begin(); i != v.end(); i++) { if (i->handler_name == h) { if (is_write && i->can_write || !is_write && i->can_read) exists = true; else exists = false; return no_err; } } exists = false; return no_err;}stringControlSocketClient::trim(string s){ size_t start, end; for (start = 0; start < s.size() && isspace(s[start]); start++) ; /* */ for (end = s.size(); end > 0 && isspace(s[end - 1]); end--) ; /* */ if (start >= end) return ""; return s.substr(start, end - start);}#if INCLUDE_TEST_CODE#define assert_eq(e, v) do { if ((e) != (v)) { cerr << "got " << (e) << endl; } assert((e) == (v)); } while (false)#define ok(err) assert_eq(err, ControlSocketClient::no_err)#define test(x, e) do { err_t err = (x); if (err != (e)) { cerr << "wanted " << assert(0) } while (false)intmain(int argc, char **argv){ unsigned short port = 7777; unsigned long ip; if (argc > 1) ip = inet_addr(argv[1]); else ip = inet_addr("127.0.0.1"); if (argc > 2) port = (unsigned short) atoi(argv[2]); typedef ControlSocketClient csc_t; csc_t cs; typedef csc_t::err_t err_t; err_t err = cs.configure(ip, port); ok(err); cout << "Router name: ``" << cs.name() << "''" << endl; string s; err = cs.get_router_version(s); ok(err); cout << "Router version: ``" << s << "''" << endl; vector<string> vs; err = cs.get_config_el_names(vs); ok(err); cout << "Elements:" << endl; for (size_t i = 0; i < vs.size(); i++) cout << i << " ``" << vs[i] << "''" << endl; err = cs.get_router_classes(vs); ok(err); cout << "Classes:" << endl; for (size_t i = 0; i < vs.size(); i++) cout << i << " ``" << vs[i] << "''" << endl; err = cs.get_router_packages(vs); ok(err); cout << "Packages:" << endl; for (size_t i = 0; i < vs.size(); i++) cout << i << " ``" << vs[i] << "''" << endl; err = cs.get_config_reqs(vs); ok(err); cout << "Configuration requirements:" << endl; for (size_t i = 0; i < vs.size(); i++) cout << i << " ``" << vs[i] << "''" << endl; err = cs.get_router_config(s); ok(err); cout << endl; cout << "*************** Begin Router Configuration *******************" << endl; cout << s; cout << "*************** End Router Configuration *******************" << endl; err = cs.get_router_flat_config(s); ok(err); cout << endl; cout << "*************** Begin Flattened Router Configuration *******************" << endl; cout << s; cout << "*************** End Flattened Router Configuration *******************" << endl; cout << endl; cout << "Handler info test: " << endl; vector<csc_t::handler_info_t> vhi; /* NB: this test assumes that there are some interesting elements in the configuration */ err = cs.get_config_el_names(vs); ok(err); if (vs.size() == 0) { cout << "No elements to test handler info on, exiting tests early" << endl; return 0; } string el = vs[0]; err = cs.get_el_handlers(el, vhi); for (size_t i = 0; i < vhi.size(); i++) cout << vhi[i].element_name << "." << vhi[i].handler_name << "\t" << (vhi[i].can_read ? "r" : "") << (vhi[i].can_write ? "w" : "") << endl; cout << endl; cout << "Check Handler test: " << endl; for (size_t i = 0; i < vhi.size(); i++) { cout << vhi[i].element_name << "." << vhi[i].handler_name << '\t'; bool res; /* is writeable? */ err = cs.check_handler(vhi[i].element_name, vhi[i].handler_name, true, res); ok(err); cout << "is_write:" << ((res == vhi[i].can_write) ? "pass" : "FAIL") << '\t'; /* is readable? */ err = cs.check_handler(vhi[i].element_name, vhi[i].handler_name, false, res); ok(err); cout << "is_read:" << ((res == vhi[i].can_read) ? "pass" : "FAIL") << endl; } cout << endl; cout << "Read/Write handler test: "; string data = "1234567891abcdefghij"; /* * NB: to place spaces in this handler's data requires that the * string be quoted; however, the handler's read function won't * return the quotes around the string so the read value doesn't * exactly match the write value. to avoid, we don't use spaces.... */ err = cs.write("InfiniteSource@1", "data", data); ok(err); string data2; err = cs.read("InfiniteSource@1", "data", data2); ok(err); if (data2 != data) cout << "FAIL (wanted ``" << data << "'', but got ``" << data2 << "'')"; else cout << "pass"; cout << endl; cout << endl << endl << "********** Tests complete **********" << endl; return 0;}#endif;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -