WvStreams
uniconfdaemonconn.cc
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 }