WvStreams
|
00001 /* 00002 * Worldvisions Weaver Software: 00003 * Copyright (C) 1997-2002 Net Integration Technologies, Inc. 00004 * 00005 * Manages a UniConf daemon session. 00006 */ 00007 #include "uniconfdaemonconn.h" 00008 #include "uniconfdaemon.h" 00009 #include "wvtclstring.h" 00010 #include "wvstrutils.h" 00011 00012 00013 /***** UniConfDaemonConn *****/ 00014 00015 UniConfDaemonConn::UniConfDaemonConn(WvStream *_s, const UniConf &_root) 00016 : UniClientConn(_s), root(_root) 00017 { 00018 uses_continue_select = true; 00019 addcallback(); 00020 writecmd(EVENT_HELLO, 00021 spacecat(wvtcl_escape("UniConf Server ready."), 00022 wvtcl_escape(UNICONF_PROTOCOL_VERSION))); 00023 } 00024 00025 00026 UniConfDaemonConn::~UniConfDaemonConn() 00027 { 00028 close(); 00029 terminate_continue_select(); 00030 delcallback(); 00031 } 00032 00033 00034 void UniConfDaemonConn::close() 00035 { 00036 UniClientConn::close(); 00037 } 00038 00039 00040 void UniConfDaemonConn::addcallback() 00041 { 00042 root.add_callback(this, wv::bind(&UniConfDaemonConn::deltacallback, this, 00043 _1, _2), true); 00044 } 00045 00046 00047 void UniConfDaemonConn::delcallback() 00048 { 00049 root.del_callback(this, true); 00050 } 00051 00052 00053 void UniConfDaemonConn::execute() 00054 { 00055 UniClientConn::execute(); 00056 00057 WvString command_string; 00058 UniClientConn::Command command = readcmd(command_string); 00059 00060 if (command != UniClientConn::NONE) 00061 { 00062 // parse and execute command 00063 WvString arg1(readarg()); 00064 WvString arg2(readarg()); 00065 switch (command) 00066 { 00067 case UniClientConn::NONE: 00068 break; 00069 00070 case UniClientConn::INVALID: 00071 do_invalid(command_string); 00072 break; 00073 00074 case UniClientConn::REQ_NOOP: 00075 do_noop(); 00076 break; 00077 00078 case UniClientConn::REQ_GET: 00079 if (arg1.isnull()) 00080 do_malformed(command); 00081 else 00082 do_get(arg1); 00083 break; 00084 00085 case UniClientConn::REQ_SET: 00086 if (arg1.isnull() || arg2.isnull()) 00087 do_malformed(command); 00088 else 00089 do_set(arg1, arg2); 00090 break; 00091 00092 case UniClientConn::REQ_REMOVE: 00093 if (arg1.isnull()) 00094 do_malformed(command); 00095 else 00096 do_remove(arg1); 00097 break; 00098 00099 case UniClientConn::REQ_SUBTREE: 00100 if (arg1.isnull()) 00101 do_malformed(command); 00102 else 00103 do_subtree(arg1, arg2.num() == 1); 00104 break; 00105 00106 case UniClientConn::REQ_HASCHILDREN: 00107 if (arg1.isnull()) 00108 do_malformed(command); 00109 else 00110 do_haschildren(arg1); 00111 break; 00112 00113 case UniClientConn::REQ_COMMIT: 00114 do_commit(); 00115 break; 00116 00117 case UniClientConn::REQ_REFRESH: 00118 do_refresh(); 00119 break; 00120 00121 case UniClientConn::REQ_QUIT: 00122 do_quit(); 00123 break; 00124 00125 case UniClientConn::REQ_HELP: 00126 do_help(); 00127 break; 00128 00129 default: 00130 do_invalid(command_string); 00131 break; 00132 } 00133 } 00134 } 00135 00136 00137 void UniConfDaemonConn::do_invalid(WvStringParm c) 00138 { 00139 writefail(WvString("unknown command: %s", c)); 00140 } 00141 00142 00143 void UniConfDaemonConn::do_malformed(UniClientConn::Command c) 00144 { 00145 writefail(WvString("malformed request: %s", 00146 UniClientConn::cmdinfos[c].name)); 00147 } 00148 00149 00150 void UniConfDaemonConn::do_noop() 00151 { 00152 writeok(); 00153 } 00154 00155 00156 void UniConfDaemonConn::do_reply(WvStringParm reply) 00157 { 00158 writefail("unexpected reply"); 00159 } 00160 00161 00162 void UniConfDaemonConn::do_get(const UniConfKey &key) 00163 { 00164 WvString value(root[key].getme()); 00165 00166 if (value.isnull()) 00167 writefail(); 00168 else 00169 writeonevalue(key, value); 00170 } 00171 00172 00173 void UniConfDaemonConn::do_set(const UniConfKey &key, WvStringParm value) 00174 { 00175 root[key].setme(value); 00176 } 00177 00178 00179 void UniConfDaemonConn::do_remove(const UniConfKey &_key) 00180 { 00181 int notifications_sent = 0; 00182 bool single_key = true; 00183 00184 // Remove '/' at the end of the key 00185 WvString strkey = _key; 00186 for (int n = strkey.len()-1; n > 0; n--) 00187 { 00188 if (strkey.edit()[n] == '/') 00189 strkey.edit()[n] = ' '; 00190 else 00191 break; 00192 } 00193 00194 trim_string(strkey.edit()); 00195 00196 UniConfKey key = strkey; 00197 00198 // Remove keys one at a time 00199 UniConf cfg(root[key]); 00200 00201 if (cfg.exists()) 00202 { 00203 UniConf::RecursiveIter it(cfg); 00204 for (it.rewind(); it.next(); ) 00205 { 00206 single_key = false; 00207 WvString sect_name = getdirname(it->fullkey()); 00208 root[it->fullkey()].remove(); 00209 00210 if (sect_name == ".") 00211 sect_name = WvString::null; 00212 00213 if (!root[sect_name].haschildren()) 00214 root[sect_name].remove(); 00215 00216 // Don't hog the daemon while delivering notifications 00217 if (++notifications_sent > CONTINUE_SELECT_AT) 00218 { 00219 notifications_sent = 0; 00220 00221 if (isok()) 00222 continue_select(0); 00223 } 00224 } 00225 00226 if (single_key) 00227 root[key].remove(); 00228 } 00229 } 00230 00231 00232 void UniConfDaemonConn::do_subtree(const UniConfKey &key, bool recursive) 00233 { 00234 static int niceness = 0; 00235 00236 UniConf cfg(root[key]); 00237 if (cfg.exists()) 00238 { 00239 if (recursive) 00240 { 00241 UniConf::RecursiveIter it(cfg); 00242 for (it.rewind(); it.next(); ) 00243 { 00244 writevalue(it->fullkey(cfg), it._value()); 00245 00246 // the output might be totally gigantic. Don't hog the 00247 // entire daemon while fulfilling it; give up our timeslice 00248 // after each entry. 00249 if (!isok()) break; 00250 if (++niceness > CONTINUE_SELECT_AT) 00251 { 00252 niceness = 0; 00253 continue_select(0); 00254 } 00255 } 00256 } 00257 else 00258 { 00259 UniConf::Iter it(cfg); 00260 for (it.rewind(); it.next(); ) 00261 { 00262 writevalue(it->fullkey(cfg), it._value()); 00263 00264 // the output might be totally gigantic. Don't hog the 00265 // entire daemon while fulfilling it; give up our timeslice 00266 // after each entry. 00267 if (!isok()) break; 00268 continue_select(0); 00269 } 00270 } 00271 writeok(); 00272 } 00273 else 00274 writefail(); 00275 } 00276 00277 void UniConfDaemonConn::do_haschildren(const UniConfKey &key) 00278 { 00279 bool haschild = root[key].haschildren(); 00280 writecmd(REPLY_CHILD, 00281 spacecat(wvtcl_escape(key), haschild ? "TRUE" : "FALSE")); 00282 } 00283 00284 00285 void UniConfDaemonConn::do_commit() 00286 { 00287 root.commit(); 00288 writeok(); 00289 } 00290 00291 00292 void UniConfDaemonConn::do_refresh() 00293 { 00294 if (root.refresh()) 00295 writeok(); 00296 else 00297 writefail(); 00298 } 00299 00300 00301 void UniConfDaemonConn::do_quit() 00302 { 00303 writeok(); 00304 close(); 00305 } 00306 00307 00308 void UniConfDaemonConn::do_help() 00309 { 00310 for (int i = 0; i < UniClientConn::NUM_COMMANDS; ++i) 00311 writetext(UniClientConn::cmdinfos[i].description); 00312 writeok(); 00313 } 00314 00315 00316 void UniConfDaemonConn::deltacallback(const UniConf &cfg, const UniConfKey &key) 00317 { 00318 // for now, we just send notifications for *any* key that changes. 00319 // Eventually we probably want to do something about having each 00320 // connection specify exactly which keys it cares about. 00321 WvString value(cfg[key].getme()); 00322 WvString msg; 00323 00324 UniConfKey fullkey(cfg.fullkey(cfg)); 00325 fullkey.append(key); 00326 00327 if (value.isnull()) 00328 msg = wvtcl_escape(fullkey); 00329 else 00330 msg = spacecat(wvtcl_escape(fullkey), 00331 wvtcl_escape(cfg[key].getme())); 00332 00333 writecmd(UniClientConn::EVENT_NOTICE, msg); 00334 }