WvStreams
uniclientgen.cc
1/*
2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2002 Net Integration Technologies, Inc.
4 *
5 * UniClientGen is a UniConfGen for retrieving data from the
6 * UniConfDaemon.
7 */
8#include "uniclientgen.h"
9#include "unilistiter.h"
10#include "wvaddr.h"
11#include "wvfile.h"
12#include "wvlinkerhack.h"
13#include "wvmoniker.h"
14#include "wvresolver.h"
15#include "wvsslstream.h"
16#include "wvstrutils.h"
17#include "wvstringmask.h"
18#include "wvtclstring.h"
19#include "wvtcp.h"
20
21WV_LINK(UniClientGen);
22
23
24#ifndef _WIN32
25#include "wvunixsocket.h"
26static IUniConfGen *unixcreator(WvStringParm s, IObject *)
27{
28 WvConstInPlaceBuf buf(s, s.len());
29 WvString dst(wvtcl_getword(buf));
30 if (!dst) dst = "";
31
32 return new UniClientGen(new WvUnixConn(dst), dst);
33}
34static WvMoniker<IUniConfGen> unixreg("unix", unixcreator);
35#endif
36
37
38static IUniConfGen *tcpcreator(WvStringParm _s, IObject *)
39{
40 WvConstInPlaceBuf buf(_s, _s.len());
41 WvString dst(wvtcl_getword(buf));
42 if (!dst) dst = "";
43
44 WvString s = dst;
45 char *cptr = s.edit();
46
47 if (!strchr(cptr, ':')) // no default port
48 s.append(":%s", DEFAULT_UNICONF_DAEMON_TCP_PORT);
49
50 return new UniClientGen(new WvTCPConn(s), dst);
51}
52
53
54static IUniConfGen *sslcreator(WvStringParm _s, IObject *)
55{
56 WvConstInPlaceBuf buf(_s, _s.len());
57 WvString dst(wvtcl_getword(buf));
58 if (!dst) dst = "";
59
60 WvString s = dst;
61 char *cptr = s.edit();
62
63 if (!strchr(cptr, ':')) // no default port
64 s.append(":%s", DEFAULT_UNICONF_DAEMON_SSL_PORT);
65
66 return new UniClientGen(new WvSSLStream(new WvTCPConn(s), NULL), dst);
67}
68
69
70static IUniConfGen *wvstreamcreator(WvStringParm s, IObject *_obj)
71{
72 return new UniClientGen(wvcreate<IWvStream>(s, _obj));
73}
74
75#ifdef WITH_SLP
76#include "wvslp.h"
77
78// FIXME: Only gets the first
79static IUniConfGen *slpcreator(WvStringParm s, IObject *)
80{
81 WvStringList serverlist;
82
83 if (slp_get_servs("uniconf.niti", serverlist))
84 {
85 WvString server = serverlist.popstr();
86 printf("Creating connection to: %s\n", server.cstr());
87 return new UniClientGen(new WvTCPConn(server), s);
88 }
89
90 return NULL;
91}
92
93static WvMoniker<IUniConfGen> slpreg("slp", slpcreator);
94#endif
95
96static WvMoniker<IUniConfGen> tcpreg("tcp", tcpcreator);
97static WvMoniker<IUniConfGen> sslreg("ssl", sslcreator);
98static WvMoniker<IUniConfGen> wvstreamreg1("wvstream", wvstreamcreator);
99static WvMoniker<IUniConfGen> wvstreamreg2("wv", wvstreamcreator);
100
101
102
103
104/***** UniClientGen *****/
105
107 : log(WvString("UniClientGen to %s",
108 dst.isnull() && stream->src()
109 ? *stream->src() : WvString(dst))),
110 timeout(60*1000),
111 version(0)
112{
113 cmdinprogress = cmdsuccess = false;
114 result_list = NULL;
115
116 conn = new UniClientConn(stream, dst);
117 conn->setcallback(wv::bind(&UniClientGen::conncallback, this));
118 WvIStreamList::globallist.append(conn, false, "uniclientconn-via-gen");
119}
120
121
122UniClientGen::~UniClientGen()
123{
124 if (isok())
126 WvIStreamList::globallist.unlink(conn);
127 WVRELEASE(conn);
128}
129
130
131time_t UniClientGen::set_timeout(time_t _timeout)
132{
133 if (_timeout < 1000)
134 timeout = 1000;
135 else
136 timeout = _timeout;
137 return timeout;
138}
139
140
142{
143 return (conn && conn->isok());
144}
145
146
148{
150 return do_select();
151}
152
154{
155 // this ensures that all keys pending notifications are dealt with
156 while (conn->isok() && conn->isreadable())
157 conn->callback();
158}
159
161{
163 do_select();
164}
165
167{
168 WvString value;
170
171 if (do_select())
172 {
173 if (result_key == key)
174 value = result;
175// else
176// seterror("Error: server sent wrong key pair.");
177 }
178 return value;
179}
180
181
182void UniClientGen::set(const UniConfKey &key, WvStringParm newvalue)
183{
184 //set_queue.append(new WvString(key), true);
185 hold_delta();
186
187 if (newvalue.isnull())
189 else
192 wvtcl_escape(newvalue), ' '));
193
195 unhold_delta();
196}
197
198
199void UniClientGen::setv(const UniConfPairList &pairs)
200{
201 hold_delta();
202
203 UniConfPairList::Iter i(pairs);
204 if (version >= 19)
205 {
206 // Much like how VAL works, SETV continues sending key-value pairs
207 // until it sends a terminating SETV, which has no arguments.
208 for (i.rewind(); i.next(); )
209 {
211 spacecat(wvtcl_escape(i->key()),
212 wvtcl_escape(i->value()), ' '));
213 }
215 }
216 else
217 {
218 for (i.rewind(); i.next(); )
219 {
220 set(i->key(), i->value());
221 }
222 }
223
224 unhold_delta();
225}
226
227
229{
231
232 if (do_select())
233 {
234 if (result_key == key && result == "TRUE")
235 return true;
236 }
237
238 return false;
239}
240
241
242UniClientGen::Iter *UniClientGen::do_iterator(const UniConfKey &key,
243 bool recursive)
244{
245 assert(!result_list);
246 result_list = new UniListIter(this);
248 WvString("%s %s", wvtcl_escape(key), WvString(recursive)));
249
250 if (do_select())
251 {
252 ListIter *it = result_list;
253 result_list = NULL;
254 return it;
255 }
256 else
257 {
258 delete result_list;
259 result_list = NULL;
260 return NULL;
261 }
262}
263
264
266{
267 return do_iterator(key, false);
268}
269
270
272{
273 return do_iterator(key, true);
274}
275
276
277void UniClientGen::conncallback()
278{
279 UniClientConn::Command command = conn->readcmd();
280 static const WvStringMask nasty_space(' ');
281 switch (command)
282 {
284 // do nothing
285 break;
286
288 cmdsuccess = true;
289 cmdinprogress = false;
290 break;
291
293 result_key = WvString::null;
294 cmdsuccess = false;
295 cmdinprogress = false;
296 break;
297
299 {
300 WvString key(wvtcl_getword(conn->payloadbuf, nasty_space));
301 WvString value(wvtcl_getword(conn->payloadbuf, nasty_space));
302
303 if (!key.isnull() && !value.isnull())
304 {
305 result_key = key;
306 result = value;
307 cmdsuccess = true;
308 }
309 cmdinprogress = false;
310 break;
311
312 }
313
315 {
316 WvString key(wvtcl_getword(conn->payloadbuf, nasty_space));
317 WvString value(wvtcl_getword(conn->payloadbuf, nasty_space));
318
319 if (!key.isnull() && !value.isnull())
320 {
321 result_key = key;
322 result = value;
323 cmdsuccess = true;
324 }
325
326 cmdinprogress = false;
327 break;
328 }
329
331 {
332 WvString key(wvtcl_getword(conn->payloadbuf, nasty_space));
333 WvString value(wvtcl_getword(conn->payloadbuf, nasty_space));
334
335 if (!key.isnull() && !value.isnull())
336 {
337 if (result_list)
338 result_list->add(key, value);
339 }
340 break;
341 }
342
344 {
345 WvStringList greeting;
346 wvtcl_decode(greeting, conn->payloadbuf.getstr(), nasty_space);
347 WvString server(greeting.popstr());
348 WvString version_string(greeting.popstr());
349
350 if (server.isnull() || strncmp(server, "UniConf", 7))
351 {
352 // wrong type of server!
353 log(WvLog::Error, "Connected to a non-UniConf server!\n");
354
355 cmdinprogress = false;
356 cmdsuccess = false;
357 conn->close();
358 }
359 else
360 {
361 version = 0;
362 sscanf(version_string, "%d", &version);
363 log(WvLog::Debug3, "UniConf version %s.\n", version);
364 }
365 break;
366 }
367
369 {
370 WvString key(wvtcl_getword(conn->payloadbuf, nasty_space));
371 WvString value(wvtcl_getword(conn->payloadbuf, nasty_space));
372 delta(key, value);
373 }
374
375 default:
376 // discard unrecognized commands
377 break;
378 }
379}
380
381
382// FIXME: horribly horribly evil!!
383bool UniClientGen::do_select()
384{
385 wvstime_sync();
386
387 hold_delta();
388
389 cmdinprogress = true;
390 cmdsuccess = false;
391
392 time_t remaining = timeout;
393 const time_t clock_error = 10*1000;
394 WvTime timeout_at = msecadd(wvstime(), timeout);
395 while (conn->isok() && cmdinprogress)
396 {
397 // We would really like to run the "real" wvstreams globallist
398 // select loop here, but we can't because we may already be inside
399 // someone else's callback or something. So we'll wait on *only* this
400 // connection.
401 //
402 // We could do this using alarm()s, but because of very strage behaviour
403 // due to inherit_request in post_select when calling the long WvStream::select()
404 // prototype as we do here we have to do the remaining stuff outselves
405 time_t last_remaining = remaining;
406 bool result = conn->select(remaining, true, false);
407 remaining = msecdiff(timeout_at, wvstime());
408 if (result)
409 conn->callback();
410 else if (remaining <= 0 && remaining > -clock_error)
411 {
412 log(WvLog::Warning, "Command timeout; connection closed.\n");
413 cmdinprogress = false;
414 cmdsuccess = false;
415 conn->close();
416 }
417
418 if (result
419 || remaining <= -clock_error
420 || remaining >= last_remaining + clock_error)
421 {
422 if (!result)
423 log(WvLog::Debug,
424 "Clock appears to have jumped; resetting"
425 " connection remaining.\n");
426 remaining = timeout;
427 timeout_at = msecadd(wvstime(), timeout);
428 }
429 }
430
431// if (!cmdsuccess)
432// seterror("Error: server timed out on response.");
433
434 unhold_delta();
435
436 return cmdsuccess;
437}
The basic interface which is included by all other XPLC interfaces and objects.
An abstract data container that backs a UniConf tree.
::UniListIter ListIter
An iterator over a constant list of keys (see below)
Represents a connection to a UniConf daemon via any WvStream.
virtual void close()
Close this stream.
Command readcmd()
Reads a command from the connection.
void writecmd(Command command, WvStringParm payload=WvString::null)
Writes a command to the connection.
Communicates with a UniConfDaemon to fetch and store keys and values.
virtual Iter * recursiveiterator(const UniConfKey &key)
Like iterator(), but the returned iterator is recursive, that is, it will return children of the imme...
virtual bool isok()
Determines if the generator is usable and working properly.
virtual void flush_buffers()
Flushes any commitment/notification buffers .
virtual void setv(const UniConfPairList &pairs)
Stores multiple key-value pairs into the registry.
virtual void set(const UniConfKey &key, WvStringParm value)
Stores a string value for a key into the registry.
virtual bool haschildren(const UniConfKey &key)
Returns true if a key has children.
UniClientGen(IWvStream *stream, WvStringParm dst=WvString::null)
Creates a generator which can communicate with a daemon using the specified stream.
virtual Iter * iterator(const UniConfKey &key)
Returns an iterator over the children of the specified key.
virtual WvString get(const UniConfKey &key)
Fetches a string value for a key from the registry.
virtual bool refresh()
Refreshes information about a key recursively.
virtual void commit()
Commits any changes.
An abstract iterator over keys and values in a generator.
void hold_delta()
Pauses notifications until matched with a call to unhold_delta().
Definition uniconfgen.cc:32
void unhold_delta()
Resumes notifications when each hold_delta() has been matched.
Definition uniconfgen.cc:38
void delta(const UniConfKey &key, WvStringParm value)
Call this when a key's value or children have possibly changed.
Definition uniconfgen.cc:77
Represents a UniConf key which is a path in a hierarchy structured much like the traditional Unix fil...
An iterator that iterates through a constant list of keys.
void add(const UniConfKey &k, WvStringParm v=WvString::null)
Add a key/value pair to the list that gets returned by this iterator.
The const in place raw memory buffer type.
A WvFastString acts exactly like a WvString, but can take (const char *) strings without needing to a...
bool isnull() const
returns true if this string is null
const char * cstr() const
return a (const char *) for this string.
A type-safe version of WvMonikerBase that lets you provide create functions for object types other th...
SSL Stream, handles SSLv2, SSLv3, and TLS Methods - If you want it to be a server,...
virtual bool isok() const
return true if the stream is actually usable right now
virtual bool isreadable()
Returns true if the stream is readable.
Definition wvstream.cc:590
void setcallback(IWvStreamCallback _callfunc)
define the callback function for this stream, called whenever the callback() member is run,...
Definition wvstream.cc:1129
bool select(time_t msec_timeout)
Return true if any of the requested features are true on the stream.
virtual void callback()
if the stream has a callback function defined, call it now.
Definition wvstream.cc:401
This is a WvList of WvStrings, and is a really handy way to parse strings.
WvString popstr()
get the first string in the list, or an empty string if the list is empty.
A class used to provide a masked lookup for characters in a string.
WvString is an implementation of a simple and efficient printable-string class.
char * edit()
make the string editable, and return a non-const (char*)
WvTCPConn tries to make all outgoing connections asynchronously (in the background).
Based on (and interchangeable with) struct timeval.
WvStream-based Unix domain socket connection class.
WvString spacecat(WvStringParm a, WvStringParm b, char sep=' ', bool onesep=false)
return the string formed by concatenating string 'a' and string 'b' with the 'sep' character between ...
Definition strutils.cc:114
WvString wvtcl_escape(WvStringParm s, const WvStringMask &nasties=WVTCL_NASTY_SPACES)
tcl-escape a string.
WvString wvtcl_getword(WvBuf &buf, const WvStringMask &splitchars=WVTCL_SPLITCHARS, bool do_unescape=true)
Get a single tcl word from an input buffer, and return the rest of the buffer untouched.
void wvtcl_decode(WvList< WvString > &l, WvStringParm _s, const WvStringMask &splitchars=WVTCL_SPLITCHARS, bool do_unescape=true)
split a tcl-style list.