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 
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 }