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 
21 WV_LINK(UniClientGen);
22 
23 
24 #ifndef _WIN32
25 #include "wvunixsocket.h"
26 static 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 }
34 static WvMoniker<IUniConfGen> unixreg("unix", unixcreator);
35 #endif
36 
37 
38 static 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 
54 static 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 
70 static 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
79 static 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 
93 static WvMoniker<IUniConfGen> slpreg("slp", slpcreator);
94 #endif
95 
96 static WvMoniker<IUniConfGen> tcpreg("tcp", tcpcreator);
97 static WvMoniker<IUniConfGen> sslreg("ssl", sslcreator);
98 static WvMoniker<IUniConfGen> wvstreamreg1("wvstream", wvstreamcreator);
99 static WvMoniker<IUniConfGen> wvstreamreg2("wv", wvstreamcreator);
100 
101 
102 
103 
104 /***** UniClientGen *****/
105 
106 UniClientGen::UniClientGen(IWvStream *stream, WvStringParm dst)
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 
122 UniClientGen::~UniClientGen()
123 {
124  if (isok())
126  WvIStreamList::globallist.unlink(conn);
127  WVRELEASE(conn);
128 }
129 
130 
131 time_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 
182 void 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
191  spacecat(wvtcl_escape(key),
192  wvtcl_escape(newvalue), ' '));
193 
194  flush_buffers();
195  unhold_delta();
196 }
197 
198 
199 void 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 
242 UniClientGen::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 
277 void UniClientGen::conncallback()
278 {
279  UniClientConn::Command command = conn->readcmd();
280  static const WvStringMask nasty_space(' ');
281  switch (command)
282  {
283  case UniClientConn::NONE:
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!!
383 bool 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 }
WvString::edit
char * edit()
make the string editable, and return a non-const (char*)
Definition: wvstring.h:397
UniClientConn::REPLY_OK
@ REPLY_OK
Definition: uniclientconn.h:68
UniClientConn::EVENT_NOTICE
@ EVENT_NOTICE
Definition: uniclientconn.h:79
UniClientGen::flush_buffers
virtual void flush_buffers()
Flushes any commitment/notification buffers .
Definition: uniclientgen.cc:153
WvStringMask
A class used to provide a masked lookup for characters in a string.
Definition: wvstringmask.h:18
wvtcl_getword
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.
Definition: wvtclstring.cc:359
UniClientConn::REQ_SUBTREE
@ REQ_SUBTREE
Definition: uniclientconn.h:60
UniClientConn::payloadbuf
WvConstStringBuffer payloadbuf
Definition: uniclientconn.h:37
WvStringList::popstr
WvString popstr()
get the first string in the list, or an empty string if the list is empty.
Definition: wvstringlist.cc:55
UniClientConn::REQ_HASCHILDREN
@ REQ_HASCHILDREN
Definition: uniclientconn.h:61
UniClientGen::haschildren
virtual bool haschildren(const UniConfKey &key)
Returns true if a key has children.
Definition: uniclientgen.cc:228
UniClientConn::REPLY_ONEVAL
@ REPLY_ONEVAL
Definition: uniclientconn.h:71
wvtclstring.h
WvSSLStream
SSL Stream, handles SSLv2, SSLv3, and TLS Methods - If you want it to be a server,...
Definition: wvsslstream.h:35
WvBufBase< unsigned char >::getstr
WvString getstr()
Returns the entire buffer as a null-terminated WvString.
Definition: wvbuffer.cc:17
UniListIter
An iterator that iterates through a constant list of keys.
Definition: unilistiter.h:27
UniListIter::add
void add(const UniConfKey &k, WvStringParm v=WvString::null)
Add a key/value pair to the list that gets returned by this iterator.
Definition: unilistiter.cc:16
WvStream::select
bool select(time_t msec_timeout)
Return true if any of the requested features are true on the stream.
Definition: wvstream.h:376
WvTime
Based on (and interchangeable with) struct timeval.
Definition: wvtimeutils.h:17
IUniConfGen::ListIter
::UniListIter ListIter
An iterator over a constant list of keys (see below)
Definition: uniconfgen.h:160
IWvStream
Definition: iwvstream.h:24
WvStream::callback
virtual void callback()
if the stream has a callback function defined, call it now.
Definition: wvstream.cc:401
UniClientGen::setv
virtual void setv(const UniConfPairList &pairs)
Stores multiple key-value pairs into the registry.
Definition: uniclientgen.cc:199
UniClientGen::recursiveiterator
virtual Iter * recursiveiterator(const UniConfKey &key)
Like iterator(), but the returned iterator is recursive, that is, it will return children of the imme...
Definition: uniclientgen.cc:271
spacecat
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
UniConfGen::unhold_delta
void unhold_delta()
Resumes notifications when each hold_delta() has been matched.
Definition: uniconfgen.cc:38
UniClientConn::REQ_GET
@ REQ_GET
Definition: uniclientconn.h:56
UniClientConn
Represents a connection to a UniConf daemon via any WvStream.
Definition: uniclientconn.h:27
UniClientConn::NONE
@ NONE
Definition: uniclientconn.h:51
WvTCPConn
WvTCPConn tries to make all outgoing connections asynchronously (in the background).
Definition: wvtcp.h:39
WvString
WvString is an implementation of a simple and efficient printable-string class.
Definition: wvstring.h:329
UniClientGen::set
virtual void set(const UniConfKey &key, WvStringParm value)
Stores a string value for a key into the registry.
Definition: uniclientgen.cc:182
IUniConfGen
An abstract data container that backs a UniConf tree.
Definition: uniconfgen.h:39
WvFastString::isnull
bool isnull() const
returns true if this string is null
Definition: wvstring.h:290
wvtcl_escape
WvString wvtcl_escape(WvStringParm s, const WvStringMask &nasties=WVTCL_NASTY_SPACES)
tcl-escape a string.
Definition: wvtclstring.cc:128
UniClientGen::commit
virtual void commit()
Commits any changes.
Definition: uniclientgen.cc:160
WvStream::isreadable
virtual bool isreadable()
Returns true if the stream is readable.
Definition: wvstream.cc:590
UniClientConn::PART_VALUE
@ PART_VALUE
Definition: uniclientconn.h:74
UniClientConn::REQ_COMMIT
@ REQ_COMMIT
Definition: uniclientconn.h:62
UniClientGen::UniClientGen
UniClientGen(IWvStream *stream, WvStringParm dst=WvString::null)
Creates a generator which can communicate with a daemon using the specified stream.
Definition: uniclientgen.cc:106
wvstrutils.h
WvFastString::cstr
const char * cstr() const
return a (const char *) for this string.
Definition: wvstring.h:267
UniConfKey
Represents a UniConf key which is a path in a hierarchy structured much like the traditional Unix fil...
Definition: uniconfkey.h:38
WvMoniker
A type-safe version of WvMonikerBase that lets you provide create functions for object types other th...
Definition: wvmoniker.h:61
UniConfGen::delta
void delta(const UniConfKey &key, WvStringParm value)
Call this when a key's value or children have possibly changed.
Definition: uniconfgen.cc:77
IObject
Definition: IObject.h:65
UniClientConn::REQ_SETV
@ REQ_SETV
Definition: uniclientconn.h:58
UniClientConn::REQ_REMOVE
@ REQ_REMOVE
Definition: uniclientconn.h:59
WvConstInPlaceBuf
The const in place raw memory buffer type.
Definition: wvbuf.h:187
UniClientGen::refresh
virtual bool refresh()
Refreshes information about a key recursively.
Definition: uniclientgen.cc:147
UniClientConn::Command
Command
Definition: uniclientconn.h:49
UniClientGen
Communicates with a UniConfDaemon to fetch and store keys and values.
Definition: uniclientgen.h:28
UniClientGen::iterator
virtual Iter * iterator(const UniConfKey &key)
Returns an iterator over the children of the specified key.
Definition: uniclientgen.cc:265
WvStreamClone::isok
virtual bool isok() const
return true if the stream is actually usable right now
Definition: wvstreamclone.cc:136
UniClientConn::writecmd
void writecmd(Command command, WvStringParm payload=WvString::null)
Writes a command to the connection.
Definition: uniclientconn.cc:152
WvStream::setcallback
void setcallback(IWvStreamCallback _callfunc)
define the callback function for this stream, called whenever the callback() member is run,...
Definition: wvstream.cc:1130
UniClientConn::readcmd
Command readcmd()
Reads a command from the connection.
Definition: uniclientconn.cc:120
UniClientConn::REPLY_FAIL
@ REPLY_FAIL
Definition: uniclientconn.h:69
UniClientConn::EVENT_HELLO
@ EVENT_HELLO
Definition: uniclientconn.h:78
WvUnixConn
WvStream-based Unix domain socket connection class.
Definition: wvunixsocket.h:33
UniClientConn::REQ_QUIT
@ REQ_QUIT
Definition: uniclientconn.h:64
UniClientGen::get
virtual WvString get(const UniConfKey &key)
Fetches a string value for a key from the registry.
Definition: uniclientgen.cc:166
WvStringList
This is a WvList of WvStrings, and is a really handy way to parse strings.
Definition: wvstringlist.h:27
UniClientConn::close
virtual void close()
Close this stream.
Definition: uniclientconn.cc:70
wvtcl_decode
void wvtcl_decode(WvList< WvString > &l, WvStringParm _s, const WvStringMask &splitchars=WVTCL_SPLITCHARS, bool do_unescape=true)
split a tcl-style list.
Definition: wvtclstring.cc:386
UniClientConn::REQ_SET
@ REQ_SET
Definition: uniclientconn.h:57
UniClientConn::REPLY_CHILD
@ REPLY_CHILD
Definition: uniclientconn.h:70
UniConfGen::hold_delta
void hold_delta()
Pauses notifications until matched with a call to unhold_delta().
Definition: uniconfgen.cc:32
UniClientConn::REQ_REFRESH
@ REQ_REFRESH
Definition: uniclientconn.h:63
UniConfGen::Iter
An abstract iterator over keys and values in a generator.
Definition: uniconfgen.h:323
UniClientGen::isok
virtual bool isok()
Determines if the generator is usable and working properly.
Definition: uniclientgen.cc:141