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