/* * Worldvisions Weaver Software: * Copyright (C) 1997-2002 Net Integration Technologies, Inc. * * Manages a UniConf daemon session. */ #include "uniconfdaemonconn.h" #include "uniconfdaemon.h" #include "wvtclstring.h" #include "wvstrutils.h" /***** UniConfDaemonConn *****/ UniConfDaemonConn::UniConfDaemonConn(WvStream *_s, const UniConf &_root) : UniClientConn(_s), root(_root) { uses_continue_select = true; addcallback(); writecmd(EVENT_HELLO, spacecat(wvtcl_escape("UniConf Server ready."), wvtcl_escape(UNICONF_PROTOCOL_VERSION))); } UniConfDaemonConn::~UniConfDaemonConn() { close(); terminate_continue_select(); delcallback(); } void UniConfDaemonConn::close() { UniClientConn::close(); } void UniConfDaemonConn::addcallback() { root.add_callback(this, wv::bind(&UniConfDaemonConn::deltacallback, this, _1, _2), true); } void UniConfDaemonConn::delcallback() { root.del_callback(this, true); } void UniConfDaemonConn::execute() { UniClientConn::execute(); WvString command_string; UniClientConn::Command command = readcmd(command_string); if (command != UniClientConn::NONE) { // parse and execute command WvString arg1(readarg()); WvString arg2(readarg()); switch (command) { case UniClientConn::NONE: break; case UniClientConn::INVALID: do_invalid(command_string); break; case UniClientConn::REQ_NOOP: do_noop(); break; case UniClientConn::REQ_GET: if (arg1.isnull()) do_malformed(command); else do_get(arg1); break; case UniClientConn::REQ_SET: if (arg1.isnull() || arg2.isnull()) do_malformed(command); else do_set(arg1, arg2); break; case UniClientConn::REQ_REMOVE: if (arg1.isnull()) do_malformed(command); else do_remove(arg1); break; case UniClientConn::REQ_SUBTREE: if (arg1.isnull()) do_malformed(command); else do_subtree(arg1, arg2.num() == 1); break; case UniClientConn::REQ_HASCHILDREN: if (arg1.isnull()) do_malformed(command); else do_haschildren(arg1); break; case UniClientConn::REQ_COMMIT: do_commit(); break; case UniClientConn::REQ_REFRESH: do_refresh(); break; case UniClientConn::REQ_QUIT: do_quit(); break; case UniClientConn::REQ_HELP: do_help(); break; default: do_invalid(command_string); break; } } } void UniConfDaemonConn::do_invalid(WvStringParm c) { writefail(WvString("unknown command: %s", c)); } void UniConfDaemonConn::do_malformed(UniClientConn::Command c) { writefail(WvString("malformed request: %s", UniClientConn::cmdinfos[c].name)); } void UniConfDaemonConn::do_noop() { writeok(); } void UniConfDaemonConn::do_reply(WvStringParm reply) { writefail("unexpected reply"); } void UniConfDaemonConn::do_get(const UniConfKey &key) { WvString value(root[key].getme()); if (value.isnull()) writefail(); else writeonevalue(key, value); } void UniConfDaemonConn::do_set(const UniConfKey &key, WvStringParm value) { root[key].setme(value); } void UniConfDaemonConn::do_remove(const UniConfKey &_key) { int notifications_sent = 0; bool single_key = true; // Remove '/' at the end of the key WvString strkey = _key; for (int n = strkey.len()-1; n > 0; n--) { if (strkey.edit()[n] == '/') strkey.edit()[n] = ' '; else break; } trim_string(strkey.edit()); UniConfKey key = strkey; // Remove keys one at a time UniConf cfg(root[key]); if (cfg.exists()) { UniConf::RecursiveIter it(cfg); for (it.rewind(); it.next(); ) { single_key = false; WvString sect_name = getdirname(it->fullkey()); root[it->fullkey()].remove(); if (sect_name == ".") sect_name = WvString::null; if (!root[sect_name].haschildren()) root[sect_name].remove(); // Don't hog the daemon while delivering notifications if (++notifications_sent > CONTINUE_SELECT_AT) { notifications_sent = 0; if (isok()) continue_select(0); } } if (single_key) root[key].remove(); } } void UniConfDaemonConn::do_subtree(const UniConfKey &key, bool recursive) { static int niceness = 0; UniConf cfg(root[key]); if (cfg.exists()) { if (recursive) { UniConf::RecursiveIter it(cfg); for (it.rewind(); it.next(); ) { writevalue(it->fullkey(cfg), it._value()); // the output might be totally gigantic. Don't hog the // entire daemon while fulfilling it; give up our timeslice // after each entry. if (!isok()) break; if (++niceness > CONTINUE_SELECT_AT) { niceness = 0; continue_select(0); } } } else { UniConf::Iter it(cfg); for (it.rewind(); it.next(); ) { writevalue(it->fullkey(cfg), it._value()); // the output might be totally gigantic. Don't hog the // entire daemon while fulfilling it; give up our timeslice // after each entry. if (!isok()) break; continue_select(0); } } writeok(); } else writefail(); } void UniConfDaemonConn::do_haschildren(const UniConfKey &key) { bool haschild = root[key].haschildren(); writecmd(REPLY_CHILD, spacecat(wvtcl_escape(key), haschild ? "TRUE" : "FALSE")); } void UniConfDaemonConn::do_commit() { root.commit(); writeok(); } void UniConfDaemonConn::do_refresh() { if (root.refresh()) writeok(); else writefail(); } void UniConfDaemonConn::do_quit() { writeok(); close(); } void UniConfDaemonConn::do_help() { for (int i = 0; i < UniClientConn::NUM_COMMANDS; ++i) writetext(UniClientConn::cmdinfos[i].description); writeok(); } void UniConfDaemonConn::deltacallback(const UniConf &cfg, const UniConfKey &key) { // for now, we just send notifications for *any* key that changes. // Eventually we probably want to do something about having each // connection specify exactly which keys it cares about. WvString value(cfg[key].getme()); WvString msg; UniConfKey fullkey(cfg.fullkey(cfg)); fullkey.append(key); if (value.isnull()) msg = wvtcl_escape(fullkey); else msg = spacecat(wvtcl_escape(fullkey), wvtcl_escape(cfg[key].getme())); writecmd(UniClientConn::EVENT_NOTICE, msg); }