/ src / libserver / link.cpp
link.cpp
  1  /*
  2      EIBD eib bus access and management daemon
  3      Copyright (C) 2005-2011 Martin Koegler <mkoegler@auto.tuwien.ac.at>
  4  
  5      This program is free software; you can redistribute it and/or modify
  6      it under the terms of the GNU General Public License as published by
  7      the Free Software Foundation; either version 2 of the License, or
  8      (at your option) any later version.
  9  
 10      This program is distributed in the hope that it will be useful,
 11      but WITHOUT ANY WARRANTY; without even the implied warranty of
 12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13      GNU General Public License for more details.
 14  
 15      You should have received a copy of the GNU General Public License
 16      along with this program; if not, write to the Free Software
 17      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 18  */
 19  
 20  #include "link.h"
 21  
 22  #include <cstdio>
 23  
 24  #include "router.h"
 25  
 26  bool
 27  LinkRecv::link(LinkBasePtr next)
 28  {
 29    assert(next);
 30    if(!next->_link(std::dynamic_pointer_cast<LinkRecv>(shared_from_this())))
 31      return false;
 32    assert(send == next); // _link_ was called
 33    return true;
 34  }
 35  
 36  bool
 37  Driver::assureFilter(std::string name, bool first)
 38  {
 39    if (findFilter(name) != nullptr)
 40      return true;
 41  
 42    auto c = conn.lock();
 43    if (c == nullptr)
 44      return false;
 45  
 46    std::string sn = this->name() + '.' + name;
 47    IniSectionPtr s = static_cast<Router&>(c->router).ini.add_auto(sn);
 48    if (s == nullptr)
 49      return false;
 50    auto f = static_cast<Router&>(c->router).get_filter(c, s, name);
 51    if (f == nullptr)
 52      return false;
 53    if (!push_filter(f, first))
 54      return false;
 55  
 56    // push_filter doesn't call setup()
 57    if (!f->setup())
 58      return false;
 59    return true;
 60  }
 61  
 62  LinkConnect::~LinkConnect()
 63  {
 64    if (addr && addr_local)
 65      static_cast<Router &>(router).release_client_addr(addr);
 66  }
 67  
 68  LinkBase::LinkBase(BaseRouter&, IniSectionPtr& s, TracePtr tr) : cfg(s)
 69  {
 70    t = TracePtr(new Trace(*tr, s));
 71    t->setAuxName("Base");
 72  }
 73  
 74  std::string
 75  LinkBase::info(int)
 76  {
 77    // TODO add more introspection
 78    std::string res = "cfg:";
 79    res += cfg->name;
 80    return res;
 81  }
 82  
 83  LinkConnect_::LinkConnect_(BaseRouter& r, IniSectionPtr& c, TracePtr tr)
 84    : router(r), LinkRecv(r,c,tr)
 85  {
 86    t->setAuxName("Conn_");
 87    //Router& rt = dynamic_cast<Router&>(r);
 88  }
 89  
 90  LinkConnect::LinkConnect(BaseRouter& r, IniSectionPtr& c, TracePtr tr)
 91    : LinkConnect_(r,c,tr)
 92  {
 93    t->setAuxName("Conn");
 94    //Router& rt = dynamic_cast<Router&>(r);
 95  }
 96  
 97  const char *
 98  LinkConnect::stateName()
 99  {
100    switch(state)
101      {
102      case L_down:
103        return "down";
104      case L_going_down:
105        return ">down";
106      case L_up:
107        return "up";
108      case L_going_up:
109        return ">up";
110      case L_error:
111        return "error";
112      case L_going_error:
113        return ">error";
114      default:
115        abort();
116        return "?!?";
117      }
118  }
119  
120  void
121  LinkConnect::setState(LConnState new_state)
122  {
123    if (state == new_state)
124      return;
125  
126    LConnState old_state = state;
127    const char *osn = stateName();
128    state = new_state;
129    TRACEPRINTF(t, 5, "%s => %s", osn, stateName());
130  
131    switch(old_state)
132      {
133      case L_down:
134        switch(new_state)
135          {
136          case L_going_up:
137            start();
138            break;
139          case L_going_down: // redundant call to stop()
140            state = L_down;
141            break;
142          default:
143            goto inval;
144          }
145        break;
146      case L_going_down:
147        switch(new_state)
148          {
149          case L_error:
150            state = L_going_error;
151            break;
152          case L_down:
153            break;
154          default:
155            goto inval;
156          }
157        break;
158      case L_up:
159        switch(new_state)
160          {
161          case L_going_up: // redundant call to start()
162            state = L_up;
163            break;
164          case L_going_down:
165            stop(false);
166            break;
167          case L_down:
168            break;
169          case L_error:
170            state = L_error;
171            break;
172          default:
173            goto inval;
174          }
175        break;
176      case L_going_up:
177        switch(new_state)
178          {
179          case L_up:
180            break;
181          case L_going_down:
182            stop(false);
183            break;
184          case L_down:
185            break;
186          case L_error:
187            state = L_error;
188            break;
189          default:
190            goto inval;
191          }
192        break;
193      case L_error:
194        switch(new_state)
195          {
196          case L_error:
197            break;
198          case L_going_down:
199            state = L_error;
200            break;
201          case L_down:
202            break;
203          default:
204            goto inval;
205          }
206        break;
207      case L_going_error:
208        switch(new_state)
209          {
210          case L_down:
211          case L_error:
212            break;
213          case L_going_down:
214            state = L_going_error;
215            break;
216          default:
217            goto inval;
218          }
219        break;
220      }
221    static_cast<Router&>(router).linkStateChanged(std::dynamic_pointer_cast<LinkConnect>(shared_from_this()));
222    return;
223  
224  inval:
225    ERRORPRINTF (t, E_ERROR | 60, "invalid transition: %s => %s", osn, stateName());
226    abort();
227    return;
228  }
229  
230  void
231  LinkConnect::setAddress(eibaddr_t addr)
232  {
233    this->addr = addr;
234    this->addr_local = false;
235  }
236  bool
237  LinkConnectSingle::setup()
238  {
239    if (!LinkConnectClient::setup())
240      return false;
241    if (addr == 0)
242      addr = static_cast<Router &>(router).get_client_addr(t);
243    if (addr == 0)
244      return false;
245    return true;
246  }
247  
248  void
249  LinkConnect::start()
250  {
251    TRACEPRINTF(t, 5, "Starting");
252    send_more = true;
253    LinkConnect_::start();
254  }
255  
256  void
257  LinkConnect_::start()
258  {
259    LinkRecv::start();
260    send->start();
261  }
262  
263  void
264  LinkConnect::stop(bool err)
265  {
266    TRACEPRINTF(t, 5, "L Stopping");
267    LinkConnect_::stop(err);
268  }
269  
270  void
271  LinkConnect_::stop(bool err)
272  {
273    send->stop(err);
274    LinkRecv::stop(err);
275  }
276  
277  const std::string&
278  Filter::name()
279  {
280    return cfg->value("filter",cfg->name);
281  }
282  
283  const std::string&
284  Driver::name()
285  {
286    return cfg->value("driver",cfg->name);
287  }
288  
289  FilterPtr
290  Filter::findFilter(std::string name, bool skip_me)
291  {
292    auto r = recv.lock();
293    if (r == nullptr)
294      return nullptr;
295    if (!skip_me && this->name() == name)
296      return std::static_pointer_cast<Filter>(shared_from_this());
297    return r->findFilter(name, false);
298  }
299  
300  FilterPtr
301  Driver::findFilter(std::string name, bool skip_me)
302  {
303    auto r = recv.lock();
304    if (r == nullptr)
305      return nullptr;
306    return r->findFilter(name);
307  }
308  
309  bool
310  LineDriver::setup()
311  {
312    if(!Driver::setup())
313      return false;
314  
315    auto c = std::dynamic_pointer_cast<LinkConnect>(conn.lock());
316    if (c == nullptr)
317      return false;
318  
319    _addr = c->addr;
320    return true;
321  }
322  
323  bool
324  LinkConnect::setup()
325  {
326    if (!LinkConnect_::setup())
327      return false;
328  
329    ignore = cfg->value("ignore",false);
330    x_may_fail = cfg->value("may-fail",false);
331    x_max_retries = cfg->value("max-retries",-1);
332    x_retry_delay = cfg->value("retry-delay",1.);
333    return true;
334  }
335  
336  bool
337  LinkConnect_::setup()
338  {
339    if (!LinkRecv::setup())
340      return false;
341    DriverPtr dr = driver; // .lock();
342    if(dr == nullptr)
343      {
344        ERRORPRINTF (t, E_ERROR | 61, "No driver in %s. Refusing.", cfg->name);
345        return false;
346      }
347  
348    std::string x = cfg->value("filters","");
349    {
350      size_t pos = 0;
351      size_t comma = 0;
352      while(true)
353        {
354          comma = x.find(',',pos);
355          std::string name = x.substr(pos,comma-pos);
356          if (name.size())
357            {
358              FilterPtr link;
359              IniSectionPtr s = static_cast<Router&>(router).ini[name];
360              name = s->value("filter",name);
361              link = static_cast<Router&>(router).get_filter(std::dynamic_pointer_cast<LinkConnect_>(shared_from_this()),
362                     s, name);
363              if (link == nullptr)
364                {
365                  ERRORPRINTF (t, E_ERROR | 32, "filter '%s' not found.", name);
366                  return false;
367                }
368              if(!dr->push_filter(link))
369                {
370                  ERRORPRINTF (t, E_ERROR | 63, "Linking filter '%s' failed.", name);
371                  return false;
372                }
373            }
374          if (comma == std::string::npos)
375            break;
376          pos = comma+1;
377        }
378    }
379  
380    LinkBasePtr s = send;
381    while (s != nullptr)
382      {
383        if (!s->setup())
384          {
385            ERRORPRINTF (t, E_ERROR | 64, "%s: setup %s: failed", cfg->name, s->cfg->name);
386            return false;
387          }
388        if (s == dr)
389          break;
390        auto ps = std::dynamic_pointer_cast<Filter>(s);
391        if (ps == nullptr)
392          {
393            ERRORPRINTF (t, E_FATAL | 102, "%s: setup %s: no driver", cfg->name, s->cfg->name);
394            return false;
395          }
396        s = ps->send;
397      }
398    if (s == nullptr)
399      {
400        ERRORPRINTF (t, E_FATAL | 103, "%s: setup: no driver", cfg->name);
401        return false;
402      }
403    return true;
404  }
405  
406  void
407  LinkConnect::started()
408  {
409    setState(L_up);
410    TRACEPRINTF(t, 5, "Started");
411  }
412  
413  void
414  LinkConnect::send_Next()
415  {
416    send_more = true;
417    TRACEPRINTF(t, 6, "sendNext called, send_more set");
418    static_cast<Router&>(router).send_Next();
419  }
420  
421  void
422  LinkConnect::send_L_Data (LDataPtr l)
423  {
424    send_more = false;
425    assert (state == L_up);
426    TRACEPRINTF(t, 6, "sending, send_more clear");
427    LinkConnect_::send_L_Data(std::move(l));
428  }
429  
430  void
431  LinkConnect::stopped(bool err)
432  {
433    setState(err ? L_error : L_down);
434  }
435  
436  void
437  LinkConnect::recv_L_Data (LDataPtr l)
438  {
439    static_cast<Router&>(router).recv_L_Data(std::move(l), *this);
440  }
441  
442  bool
443  LinkConnect::checkSysAddress(eibaddr_t addr)
444  {
445    return static_cast<Router&>(router).checkAddress(addr, std::dynamic_pointer_cast<LinkConnect>(shared_from_this()));
446  }
447  
448  bool
449  LinkConnect::checkSysGroupAddress(eibaddr_t addr)
450  {
451    return static_cast<Router&>(router).checkGroupAddress(addr, std::dynamic_pointer_cast<LinkConnect>(shared_from_this()));
452  }
453  
454  bool
455  LinkConnect_::checkSysAddress(eibaddr_t addr)
456  {
457    return static_cast<Router&>(router).checkAddress(addr, nullptr);
458  }
459  
460  bool
461  LinkConnect_::checkSysGroupAddress(eibaddr_t addr)
462  {
463    return static_cast<Router&>(router).checkGroupAddress(addr, nullptr);
464  }
465  
466  
467  void
468  LinkConnect::recv_L_Busmonitor (LBusmonPtr l)
469  {
470    static_cast<Router&>(router).recv_L_Busmonitor(std::move(l));
471  }
472  
473  bool
474  Server::setup()
475  {
476    return true;
477  }
478  
479  LinkConnectClient::LinkConnectClient(ServerPtr s, IniSectionPtr& c, TracePtr tr)
480    : server(s), LinkConnect(s->router, c, tr)
481  {
482    t->setAuxName("ConnC");
483    char n[10];
484    sprintf(n,"%d",t->seq);
485    linkname = t->name + '_' + n;
486  }
487  
488  SubDriver::SubDriver(const LinkConnectClientPtr& c)
489    : BusDriver(static_cast<const LinkConnectPtr&>(c), c->cfg)
490  {
491    t->setAuxName("SubDr");
492    server = c->server;
493  }
494  
495  LineDriver::LineDriver(const LinkConnectClientPtr& c)
496    : Driver(c, c->cfg)
497  {
498    t->setAuxName("LineDr");
499    server = c->server;
500  }
501  
502  void
503  Driver::send_Next()
504  {
505    auto r = recv.lock();
506    if (r != nullptr)
507      r->send_Next();
508  }
509  
510  void
511  Driver::recv_L_Data (LDataPtr l)
512  {
513    auto r = recv.lock();
514    if (r != nullptr)
515      r->recv_L_Data(std::move(l));
516  }
517  
518  bool
519  Driver::checkSysAddress(eibaddr_t addr)
520  {
521    auto r = recv.lock();
522    if (r == nullptr)
523      return false;
524    return r->checkSysAddress(addr);
525  }
526  
527  bool
528  Driver::checkSysGroupAddress(eibaddr_t addr)
529  {
530    auto r = recv.lock();
531    if (r == nullptr)
532      return false;
533    return r->checkSysGroupAddress(addr);
534  }
535  
536  void
537  Filter::send_Next()
538  {
539    auto r = recv.lock();
540    if (r != nullptr)
541      r->send_Next();
542  }
543  
544  void
545  Filter::recv_L_Data (LDataPtr l)
546  {
547    auto r = recv.lock();
548    if (r != nullptr)
549      r->recv_L_Data(std::move(l));
550  }
551  
552  bool
553  Filter::checkSysAddress(eibaddr_t addr)
554  {
555    auto r = recv.lock();
556    if (r == nullptr)
557      return false;
558    return r->checkSysAddress(addr);
559  }
560  
561  bool
562  Filter::checkSysGroupAddress(eibaddr_t addr)
563  {
564    auto r = recv.lock();
565    if (r == nullptr)
566      return false;
567    return r->checkSysGroupAddress(addr);
568  }
569  
570  void
571  Driver::recv_L_Busmonitor (LBusmonPtr l)
572  {
573    auto r = recv.lock();
574    if (r != nullptr)
575      r->recv_L_Busmonitor(std::move(l));
576  }
577  
578  void
579  Filter::recv_L_Busmonitor (LBusmonPtr l)
580  {
581    auto r = recv.lock();
582    if (r != nullptr)
583      r->recv_L_Busmonitor(std::move(l));
584  }
585  
586  void
587  Driver::started()
588  {
589    auto r = recv.lock();
590    if (r != nullptr)
591      r->started();
592  }
593  
594  void
595  Filter::started()
596  {
597    auto r = recv.lock();
598    if (r != nullptr)
599      r->started();
600  }
601  
602  void
603  Driver::stopped(bool err)
604  {
605    auto r = recv.lock();
606    if (r != nullptr)
607      r->stopped(err);
608  }
609  
610  void
611  Filter::stopped(bool err)
612  {
613    auto r = recv.lock();
614    if (r != nullptr)
615      r->stopped(err);
616  }
617  
618  bool
619  Driver::push_filter(FilterPtr filter, bool first)
620  {
621    LinkRecvPtr r;
622    LinkBasePtr t;
623  
624    // r->t ==> r->filter->t
625  
626    if (first)
627      {
628        // r is the LinkConnect base, t is the following LinkBase-ish thing
629        LinkConnectPtr_ c = conn.lock();
630        if (c == nullptr)
631          return false;
632        r = c;
633        t = c->send;
634      }
635    else
636      {
637        // t is this driver, so r is this->recv
638        r = recv.lock();
639        if (r == nullptr)
640          return false;
641        t = shared_from_this();
642      }
643  
644    // link the first part
645    if (!r->link(filter))
646      return false;
647    // link the second part
648    if (!filter->link(t))
649      {
650        // didn't work, so undo the first.
651        r->link(t);
652        return false;
653      }
654  
655  #if 0 // this is done by LinkConnect::setup() once the stack is complete
656    if (!filter->setup())
657      {
658        filter->unlink();
659        return false;
660      }
661  #endif
662    return true;
663  }
664  
665  Filter::Filter(const LinkConnectPtr_& c, IniSectionPtr& s)
666    : LinkRecv(c->router, s, c->t)
667  {
668    conn = c;
669    t->setAuxName(c->t->name);
670  }
671