00001
00002
00003
00004
00005
00006
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, IObject *)
00027 {
00028 WvConstInPlaceBuf buf(s, s.len());
00029 WvString dst(wvtcl_getword(buf));
00030 if (!dst) dst = "";
00031
00032 return new UniClientGen(new WvUnixConn(dst), dst);
00033 }
00034 static WvMoniker<IUniConfGen> unixreg("unix", unixcreator);
00035 #endif
00036
00037
00038 static IUniConfGen *tcpcreator(WvStringParm _s, IObject *)
00039 {
00040 WvConstInPlaceBuf buf(_s, _s.len());
00041 WvString dst(wvtcl_getword(buf));
00042 if (!dst) dst = "";
00043
00044 WvString s = dst;
00045 char *cptr = s.edit();
00046
00047 if (!strchr(cptr, ':'))
00048 s.append(":%s", DEFAULT_UNICONF_DAEMON_TCP_PORT);
00049
00050 return new UniClientGen(new WvTCPConn(s), dst);
00051 }
00052
00053
00054 static IUniConfGen *sslcreator(WvStringParm _s, IObject *)
00055 {
00056 WvConstInPlaceBuf buf(_s, _s.len());
00057 WvString dst(wvtcl_getword(buf));
00058 if (!dst) dst = "";
00059
00060 WvString s = dst;
00061 char *cptr = s.edit();
00062
00063 if (!strchr(cptr, ':'))
00064 s.append(":%s", DEFAULT_UNICONF_DAEMON_SSL_PORT);
00065
00066 return new UniClientGen(new WvSSLStream(new WvTCPConn(s), NULL), dst);
00067 }
00068
00069
00070 static IUniConfGen *wvstreamcreator(WvStringParm s, IObject *_obj)
00071 {
00072 return new UniClientGen(wvcreate<IWvStream>(s, _obj));
00073 }
00074
00075 #ifdef WITH_SLP
00076 #include "wvslp.h"
00077
00078
00079 static IUniConfGen *slpcreator(WvStringParm s, IObject *)
00080 {
00081 WvStringList serverlist;
00082
00083 if (slp_get_servs("uniconf.niti", serverlist))
00084 {
00085 WvString server = serverlist.popstr();
00086 printf("Creating connection to: %s\n", server.cstr());
00087 return new UniClientGen(new WvTCPConn(server), s);
00088 }
00089
00090 return NULL;
00091 }
00092
00093 static WvMoniker<IUniConfGen> slpreg("slp", slpcreator);
00094 #endif
00095
00096 static WvMoniker<IUniConfGen> tcpreg("tcp", tcpcreator);
00097 static WvMoniker<IUniConfGen> sslreg("ssl", sslcreator);
00098 static WvMoniker<IUniConfGen> wvstreamreg1("wvstream", wvstreamcreator);
00099 static WvMoniker<IUniConfGen> wvstreamreg2("wv", wvstreamcreator);
00100
00101
00102
00103
00104
00105
00106 UniClientGen::UniClientGen(IWvStream *stream, WvStringParm dst)
00107 : log(WvString("UniClientGen to %s",
00108 dst.isnull() && stream->src()
00109 ? *stream->src() : WvString(dst))),
00110 timeout(60*1000),
00111 version(0)
00112 {
00113 cmdinprogress = cmdsuccess = false;
00114 result_list = NULL;
00115
00116 conn = new UniClientConn(stream, dst);
00117 conn->setcallback(wv::bind(&UniClientGen::conncallback, this));
00118 WvIStreamList::globallist.append(conn, false, "uniclientconn-via-gen");
00119 }
00120
00121
00122 UniClientGen::~UniClientGen()
00123 {
00124 if (isok())
00125 conn->writecmd(UniClientConn::REQ_QUIT, "");
00126 WvIStreamList::globallist.unlink(conn);
00127 WVRELEASE(conn);
00128 }
00129
00130
00131 time_t UniClientGen::set_timeout(time_t _timeout)
00132 {
00133 if (_timeout < 1000)
00134 timeout = 1000;
00135 else
00136 timeout = _timeout;
00137 return timeout;
00138 }
00139
00140
00141 bool UniClientGen::isok()
00142 {
00143 return (conn && conn->isok());
00144 }
00145
00146
00147 bool UniClientGen::refresh()
00148 {
00149 conn->writecmd(UniClientConn::REQ_REFRESH);
00150 return do_select();
00151 }
00152
00153 void UniClientGen::flush_buffers()
00154 {
00155
00156 while (conn->isok() && conn->isreadable())
00157 conn->callback();
00158 }
00159
00160 void UniClientGen::commit()
00161 {
00162 conn->writecmd(UniClientConn::REQ_COMMIT);
00163 do_select();
00164 }
00165
00166 WvString UniClientGen::get(const UniConfKey &key)
00167 {
00168 WvString value;
00169 conn->writecmd(UniClientConn::REQ_GET, wvtcl_escape(key));
00170
00171 if (do_select())
00172 {
00173 if (result_key == key)
00174 value = result;
00175
00176
00177 }
00178 return value;
00179 }
00180
00181
00182 void UniClientGen::set(const UniConfKey &key, WvStringParm newvalue)
00183 {
00184
00185 hold_delta();
00186
00187 if (newvalue.isnull())
00188 conn->writecmd(UniClientConn::REQ_REMOVE, wvtcl_escape(key));
00189 else
00190 conn->writecmd(UniClientConn::REQ_SET,
00191 spacecat(wvtcl_escape(key),
00192 wvtcl_escape(newvalue), ' '));
00193
00194 flush_buffers();
00195 unhold_delta();
00196 }
00197
00198
00199 void UniClientGen::setv(const UniConfPairList &pairs)
00200 {
00201 hold_delta();
00202
00203 UniConfPairList::Iter i(pairs);
00204 if (version >= 19)
00205 {
00206
00207
00208 for (i.rewind(); i.next(); )
00209 {
00210 conn->writecmd(UniClientConn::REQ_SETV,
00211 spacecat(wvtcl_escape(i->key()),
00212 wvtcl_escape(i->value()), ' '));
00213 }
00214 conn->writecmd(UniClientConn::REQ_SETV);
00215 }
00216 else
00217 {
00218 for (i.rewind(); i.next(); )
00219 {
00220 set(i->key(), i->value());
00221 }
00222 }
00223
00224 unhold_delta();
00225 }
00226
00227
00228 bool UniClientGen::haschildren(const UniConfKey &key)
00229 {
00230 conn->writecmd(UniClientConn::REQ_HASCHILDREN, wvtcl_escape(key));
00231
00232 if (do_select())
00233 {
00234 if (result_key == key && result == "TRUE")
00235 return true;
00236 }
00237
00238 return false;
00239 }
00240
00241
00242 UniClientGen::Iter *UniClientGen::do_iterator(const UniConfKey &key,
00243 bool recursive)
00244 {
00245 assert(!result_list);
00246 result_list = new UniListIter(this);
00247 conn->writecmd(UniClientConn::REQ_SUBTREE,
00248 WvString("%s %s", wvtcl_escape(key), WvString(recursive)));
00249
00250 if (do_select())
00251 {
00252 ListIter *it = result_list;
00253 result_list = NULL;
00254 return it;
00255 }
00256 else
00257 {
00258 delete result_list;
00259 result_list = NULL;
00260 return NULL;
00261 }
00262 }
00263
00264
00265 UniClientGen::Iter *UniClientGen::iterator(const UniConfKey &key)
00266 {
00267 return do_iterator(key, false);
00268 }
00269
00270
00271 UniClientGen::Iter *UniClientGen::recursiveiterator(const UniConfKey &key)
00272 {
00273 return do_iterator(key, true);
00274 }
00275
00276
00277 void UniClientGen::conncallback()
00278 {
00279 UniClientConn::Command command = conn->readcmd();
00280 static const WvStringMask nasty_space(' ');
00281 switch (command)
00282 {
00283 case UniClientConn::NONE:
00284
00285 break;
00286
00287 case UniClientConn::REPLY_OK:
00288 cmdsuccess = true;
00289 cmdinprogress = false;
00290 break;
00291
00292 case UniClientConn::REPLY_FAIL:
00293 result_key = WvString::null;
00294 cmdsuccess = false;
00295 cmdinprogress = false;
00296 break;
00297
00298 case UniClientConn::REPLY_CHILD:
00299 {
00300 WvString key(wvtcl_getword(conn->payloadbuf, nasty_space));
00301 WvString value(wvtcl_getword(conn->payloadbuf, nasty_space));
00302
00303 if (!key.isnull() && !value.isnull())
00304 {
00305 result_key = key;
00306 result = value;
00307 cmdsuccess = true;
00308 }
00309 cmdinprogress = false;
00310 break;
00311
00312 }
00313
00314 case UniClientConn::REPLY_ONEVAL:
00315 {
00316 WvString key(wvtcl_getword(conn->payloadbuf, nasty_space));
00317 WvString value(wvtcl_getword(conn->payloadbuf, nasty_space));
00318
00319 if (!key.isnull() && !value.isnull())
00320 {
00321 result_key = key;
00322 result = value;
00323 cmdsuccess = true;
00324 }
00325
00326 cmdinprogress = false;
00327 break;
00328 }
00329
00330 case UniClientConn::PART_VALUE:
00331 {
00332 WvString key(wvtcl_getword(conn->payloadbuf, nasty_space));
00333 WvString value(wvtcl_getword(conn->payloadbuf, nasty_space));
00334
00335 if (!key.isnull() && !value.isnull())
00336 {
00337 if (result_list)
00338 result_list->add(key, value);
00339 }
00340 break;
00341 }
00342
00343 case UniClientConn::EVENT_HELLO:
00344 {
00345 WvStringList greeting;
00346 wvtcl_decode(greeting, conn->payloadbuf.getstr(), nasty_space);
00347 WvString server(greeting.popstr());
00348 WvString version_string(greeting.popstr());
00349
00350 if (server.isnull() || strncmp(server, "UniConf", 7))
00351 {
00352
00353 log(WvLog::Error, "Connected to a non-UniConf server!\n");
00354
00355 cmdinprogress = false;
00356 cmdsuccess = false;
00357 conn->close();
00358 }
00359 else
00360 {
00361 version = 0;
00362 sscanf(version_string, "%d", &version);
00363 log(WvLog::Debug3, "UniConf version %s.\n", version);
00364 }
00365 break;
00366 }
00367
00368 case UniClientConn::EVENT_NOTICE:
00369 {
00370 WvString key(wvtcl_getword(conn->payloadbuf, nasty_space));
00371 WvString value(wvtcl_getword(conn->payloadbuf, nasty_space));
00372 delta(key, value);
00373 }
00374
00375 default:
00376
00377 break;
00378 }
00379 }
00380
00381
00382
00383 bool UniClientGen::do_select()
00384 {
00385 wvstime_sync();
00386
00387 hold_delta();
00388
00389 cmdinprogress = true;
00390 cmdsuccess = false;
00391
00392 time_t remaining = timeout;
00393 const time_t clock_error = 10*1000;
00394 WvTime timeout_at = msecadd(wvstime(), timeout);
00395 while (conn->isok() && cmdinprogress)
00396 {
00397
00398
00399
00400
00401
00402
00403
00404
00405 time_t last_remaining = remaining;
00406 bool result = conn->select(remaining, true, false);
00407 remaining = msecdiff(timeout_at, wvstime());
00408 if (result)
00409 conn->callback();
00410 else if (remaining <= 0 && remaining > -clock_error)
00411 {
00412 log(WvLog::Warning, "Command timeout; connection closed.\n");
00413 cmdinprogress = false;
00414 cmdsuccess = false;
00415 conn->close();
00416 }
00417
00418 if (result
00419 || remaining <= -clock_error
00420 || remaining >= last_remaining + clock_error)
00421 {
00422 if (!result)
00423 log(WvLog::Debug,
00424 "Clock appears to have jumped; resetting"
00425 " connection remaining.\n");
00426 remaining = timeout;
00427 timeout_at = msecadd(wvstime(), timeout);
00428 }
00429 }
00430
00431
00432
00433
00434 unhold_delta();
00435
00436 return cmdsuccess;
00437 }