uniclientgen.cc

00001 /*
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2002 Net Integration Technologies, Inc.
00004  * 
00005  * UniClientGen is a UniConfGen for retrieving data from the
00006  * UniConfDaemon.
00007  */
00008 #include "uniclientgen.h"
00009 #include "unilistiter.h"
00010 #include "wvaddr.h"
00011 #include "wvfile.h"
00012 #include "wvlinkerhack.h"
00013 #include "wvmoniker.h"
00014 #include "wvresolver.h"
00015 #include "wvsslstream.h"
00016 #include "wvstrutils.h"
00017 #include "wvstringmask.h"
00018 #include "wvtclstring.h"
00019 #include "wvtcp.h"
00020 
00021 WV_LINK(UniClientGen);
00022 
00023 
00024 #ifndef _WIN32
00025 #include "wvunixsocket.h"
00026 static IUniConfGen *unixcreator(WvStringParm s)
00027 {
00028     return new UniClientGen(new WvUnixConn(s));
00029 }
00030 static WvMoniker<IUniConfGen> unixreg("unix", unixcreator);
00031 #endif
00032 
00033 
00034 static IUniConfGen *tcpcreator(WvStringParm _s)
00035 {
00036     WvString s(_s);
00037     char *cptr = s.edit();
00038     
00039     if (!strchr(cptr, ':')) // no default port
00040         s.append(":%s", DEFAULT_UNICONF_DAEMON_TCP_PORT);
00041     
00042     return new UniClientGen(new WvTCPConn(s), _s);
00043 }
00044 
00045 
00046 static IUniConfGen *sslcreator(WvStringParm _s)
00047 {
00048     WvString s(_s);
00049     char *cptr = s.edit();
00050     
00051     if (!strchr(cptr, ':')) // no default port
00052         s.append(":%s", DEFAULT_UNICONF_DAEMON_SSL_PORT);
00053     
00054     return new UniClientGen(new WvSSLStream(new WvTCPConn(s), NULL), _s);
00055 }
00056 
00057 
00058 static IUniConfGen *wvstreamcreator(WvStringParm s)
00059 {
00060     return new UniClientGen(wvcreate<IWvStream>(s));
00061 }
00062 
00063 #ifdef WITH_SLP
00064 #include "wvslp.h"
00065 
00066 // FIXME: Only gets the first
00067 static IUniConfGen *slpcreator(WvStringParm s)
00068 {
00069     WvStringList serverlist;
00070     
00071     if (slp_get_servs("uniconf.niti", serverlist))
00072     {
00073         WvString server = serverlist.popstr();
00074         printf("Creating connection to: %s\n", server.cstr());
00075         return new UniClientGen(new WvTCPConn(server), s);
00076     }
00077     else
00078         return NULL;
00079 }
00080 
00081 static WvMoniker<IUniConfGen> slpreg("slp", slpcreator);
00082 #endif
00083 
00084 static WvMoniker<IUniConfGen> tcpreg("tcp", tcpcreator);
00085 static WvMoniker<IUniConfGen> sslreg("ssl", sslcreator);
00086 static WvMoniker<IUniConfGen> wvstreamreg("wvstream", wvstreamcreator);
00087 
00088 
00089 
00090 
00091 /***** UniClientGen *****/
00092 
00093 UniClientGen::UniClientGen(IWvStream *stream, WvStringParm dst) 
00094     : log(WvString("UniClientGen to %s",
00095                    dst.isnull() && stream->src() 
00096                    ? *stream->src() : WvString(dst))),
00097       version(0)
00098 {
00099     cmdinprogress = cmdsuccess = false;
00100     result_list = NULL;
00101 
00102     conn = new UniClientConn(stream, dst);
00103     conn->setcallback(WvStreamCallback(this,
00104         &UniClientGen::conncallback), NULL);
00105     WvIStreamList::globallist.append(conn, false, "uniclientconn-via-gen");
00106 }
00107 
00108 
00109 UniClientGen::~UniClientGen()
00110 {
00111     if (isok())
00112         conn->writecmd(UniClientConn::REQ_QUIT, "");
00113     WvIStreamList::globallist.unlink(conn);
00114     WVRELEASE(conn);
00115 }
00116 
00117 
00118 bool UniClientGen::isok()
00119 {
00120     return (conn && conn->isok());
00121 }
00122 
00123 
00124 bool UniClientGen::refresh()
00125 {
00126     conn->writecmd(UniClientConn::REQ_REFRESH);
00127     return do_select();
00128 }
00129 
00130 void UniClientGen::flush_buffers()
00131 {
00132     // this ensures that all keys pending notifications are dealt with
00133     while (conn->isok() && conn->isreadable())
00134         conn->callback();
00135 }
00136 
00137 void UniClientGen::commit()
00138 {
00139     conn->writecmd(UniClientConn::REQ_COMMIT);
00140     do_select();
00141 }
00142 
00143 WvString UniClientGen::get(const UniConfKey &key)
00144 {
00145     WvString value;
00146     conn->writecmd(UniClientConn::REQ_GET, wvtcl_escape(key));
00147 
00148     if (do_select())
00149     {
00150         if (result_key == key)
00151             value = result;
00152 //        else
00153 //            seterror("Error: server sent wrong key pair.");
00154     }
00155     return value;
00156 }
00157 
00158 
00159 void UniClientGen::set(const UniConfKey &key, WvStringParm newvalue)
00160 {
00161     //set_queue.append(new WvString(key), true);
00162     hold_delta();
00163 
00164     if (newvalue.isnull())
00165         conn->writecmd(UniClientConn::REQ_REMOVE, wvtcl_escape(key));
00166     else
00167         conn->writecmd(UniClientConn::REQ_SET,
00168                        spacecat(wvtcl_escape(key),
00169                                 wvtcl_escape(newvalue), ' '));
00170 
00171     flush_buffers();
00172     unhold_delta();
00173 }
00174 
00175 
00176 void UniClientGen::setv(const UniConfPairList &pairs)
00177 {
00178     hold_delta();
00179 
00180     UniConfPairList::Iter i(pairs);
00181     if (version >= 19)
00182     {
00183         // Much like how VAL works, SETV continues sending key-value pairs
00184         // until it sends a terminating SETV, which has no arguments.
00185         for (i.rewind(); i.next(); )
00186         {
00187             conn->writecmd(UniClientConn::REQ_SETV,
00188                            spacecat(wvtcl_escape(i->key()),
00189                                     wvtcl_escape(i->value()), ' '));
00190         }
00191         conn->writecmd(UniClientConn::REQ_SETV);
00192     }
00193     else
00194     {
00195         for (i.rewind(); i.next(); )
00196         {
00197             set(i->key(), i->value());
00198         }
00199     }
00200 
00201     unhold_delta();
00202 }
00203 
00204 
00205 bool UniClientGen::haschildren(const UniConfKey &key)
00206 {
00207     conn->writecmd(UniClientConn::REQ_HASCHILDREN, wvtcl_escape(key));
00208 
00209     if (do_select())
00210     {
00211         if (result_key == key && result == "TRUE")
00212             return true;
00213     }
00214 
00215     return false;
00216 }
00217 
00218 
00219 UniClientGen::Iter *UniClientGen::do_iterator(const UniConfKey &key,
00220                                               bool recursive)
00221 {
00222     assert(!result_list);
00223     result_list = new UniListIter(this);
00224     conn->writecmd(UniClientConn::REQ_SUBTREE,
00225                    WvString("%s %s", wvtcl_escape(key), WvString(recursive)));
00226 
00227     if (do_select())
00228     {
00229         ListIter *it = result_list;
00230         result_list = NULL;
00231         return it;
00232     }
00233     else
00234     {
00235         delete result_list;
00236         result_list = NULL;
00237         return NULL;
00238     }
00239 }
00240 
00241 
00242 UniClientGen::Iter *UniClientGen::iterator(const UniConfKey &key)
00243 {
00244     return do_iterator(key, false);
00245 }
00246     
00247 
00248 UniClientGen::Iter *UniClientGen::recursiveiterator(const UniConfKey &key)
00249 {
00250     return do_iterator(key, true);
00251 }
00252 
00253 
00254 time_t uptime()
00255 {
00256     WvFile f("/proc/uptime", O_RDONLY);
00257     if (f.isok())
00258         return WvString(f.getline(0)).num();
00259     return 0;
00260 }
00261 
00262 
00263 void UniClientGen::conncallback(WvStream &stream, void *userdata)
00264 {
00265     UniClientConn::Command command = conn->readcmd();
00266     static const WvStringMask nasty_space(' ');
00267     switch (command)
00268     {
00269         case UniClientConn::NONE:
00270             // do nothing
00271             break;
00272 
00273         case UniClientConn::REPLY_OK:
00274             cmdsuccess = true;
00275             cmdinprogress = false;
00276             break;
00277 
00278         case UniClientConn::REPLY_FAIL:
00279             result_key = WvString::null;
00280             cmdsuccess = false;
00281             cmdinprogress = false;
00282             break;
00283 
00284         case UniClientConn::REPLY_CHILD:
00285             {
00286                 WvString key(wvtcl_getword(conn->payloadbuf, nasty_space));
00287                 WvString value(wvtcl_getword(conn->payloadbuf, nasty_space));
00288 
00289                 if (!key.isnull() && !value.isnull())
00290                 {
00291                     result_key = key;
00292                     result = value;
00293                     cmdsuccess = true;
00294                 }
00295                 cmdinprogress = false;
00296                 break;
00297 
00298             }
00299 
00300         case UniClientConn::REPLY_ONEVAL:
00301             {
00302                 WvString key(wvtcl_getword(conn->payloadbuf, nasty_space));
00303                 WvString value(wvtcl_getword(conn->payloadbuf, nasty_space));
00304 
00305                 if (!key.isnull() && !value.isnull())
00306                 {
00307                     result_key = key;
00308                     result = value;
00309                     cmdsuccess = true;
00310                 }
00311 
00312                 cmdinprogress = false;
00313                 break;
00314             }
00315 
00316         case UniClientConn::PART_VALUE:
00317             {
00318                 WvString key(wvtcl_getword(conn->payloadbuf, nasty_space));
00319                 WvString value(wvtcl_getword(conn->payloadbuf, nasty_space));
00320 
00321                 if (!key.isnull() && !value.isnull())
00322                 {
00323                     if (result_list)
00324                         result_list->add(key, value);
00325                 }
00326                 break;
00327             }
00328 
00329         case UniClientConn::EVENT_HELLO:
00330             {
00331                 WvStringList greeting;
00332                 wvtcl_decode(greeting, conn->payloadbuf.getstr(), nasty_space);
00333                 WvString server(greeting.popstr());
00334                 WvString version_string(greeting.popstr());
00335 
00336                 if (server.isnull() || strncmp(server, "UniConf", 7))
00337                 {
00338                     // wrong type of server!
00339                     log(WvLog::Error, "Connected to a non-UniConf serrer!\n");
00340 
00341                     cmdinprogress = false;
00342                     cmdsuccess = false;
00343                     conn->close();
00344                 }
00345                 else
00346                 {
00347                     version = 0;
00348                     sscanf(version_string, "%d", &version);
00349                     log(WvLog::Debug3, "UniConf version %s.\n", version);
00350                 }
00351                 break;
00352             }
00353 
00354         case UniClientConn::EVENT_NOTICE:
00355             {
00356                 WvString key(wvtcl_getword(conn->payloadbuf, nasty_space));
00357                 WvString value(wvtcl_getword(conn->payloadbuf, nasty_space));
00358                 delta(key, value);
00359             }   
00360 
00361         default:
00362             // discard unrecognized commands
00363             break;
00364     }
00365 }
00366 
00367 
00368 // FIXME: horribly horribly evil!!
00369 bool UniClientGen::do_select()
00370 {
00371     hold_delta();
00372     
00373     cmdinprogress = true;
00374     cmdsuccess = false;
00375 
00376     timeout_activity = uptime();
00377     while (conn->isok() && cmdinprogress)
00378     {
00379         // We would really like to run the "real" wvstreams globallist
00380         // select loop here, but we can't because we may already be inside
00381         // someone else's callback or something.  So we'll wait on *only* this
00382         // connection.
00383         //
00384         // We could do this using alarm()s, but because of very strage behaviour
00385         // due to inherit_request in post_select when calling the long WvStream::select()
00386         // prototype as we do here we have to do the timeout stuff outselves
00387         if (conn->select(TIMEOUT, true, false))
00388         {
00389             conn->callback();
00390             timeout_activity = uptime();
00391         }
00392         else if (timeout_activity+3*(TIMEOUT/1000)/4 <= uptime())
00393         {
00394             // command response took too long!
00395             log(WvLog::Warning, "Command timeout; connection closed.\n");
00396             cmdinprogress = false;
00397             cmdsuccess = false;
00398             conn->close();
00399         }
00400     }
00401 
00402 //    if (!cmdsuccess)
00403 //        seterror("Error: server timed out on response.");
00404 
00405     unhold_delta();
00406     
00407     return cmdsuccess;
00408 }

Generated on Wed Jul 12 17:53:19 2006 for WvStreams by  doxygen 1.4.7