/** * Copyright - See the COPYRIGHT that is included with this distribution. * pvxs is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. */ #include #include #include #include #include #include #include #include #include #if !defined(_WIN32) #include #define USE_SIGNAL #endif #include #include #include #include #include #include #include namespace pva = pvxsimpl; namespace { DEFINE_LOGGER(out, "pvxvct"); epicsEvent done; #ifdef USE_SIGNAL void alldone(int num) { (void)num; done.signal(); } #endif // parse hostname, IP, or IP+netmask std::tuple parsePeer(const char *optarg) { // nameorip // nameorip/## // nameorip/###.###.###.### // static is safe as we only use from main() thread static std::regex netname("([^/:]*)(?:/([0-9.]+))?"); std::cmatch match; if(!std::regex_match(optarg, match, netname)) { throw std::runtime_error(pva::SB()<<"Expected host name or IP range. not "<>nbit).good()) { throw std::runtime_error(pva::SB()<<"Expected number of bits. not "<> peers; std::set pvnames; } opts; std::vector bindaddrs; { int opt; while ((opt = getopt(argc, argv, "hCSH:B:P:")) != -1) { switch(opt) { case 'h': usage(argv[0]); return 0; default: usage(argv[0]); std::cerr<<"\nUnknown argument: "<sa, &slen)) { throw std::runtime_error(pva::SB()<<"Expected address[:port] to bind. Not "<in.sin_addr.s_addr&mask)==addr) { match = true; break; } } if(!match) return; } bool showpeer=false; auto lazypeer = [&showpeer, &msg]() { if(!showpeer) log_printf(out, PLVL_INFO, "From %s\n", msg.src.tostring().c_str()); showpeer = true; }; // allow that one UDP packet may contain several PVA messages for(unsigned i=0; !msg.msgs[i].empty(); i++) { auto M = msg.msgs[i]; auto be = M[2]&pva::pva_flags::MSB; auto cmd = M[3]; M+=4; // skip header uint32_t blen; pva::from_wire(M, blen, be); switch(cmd) { case pva::pva_app_msg::OriginTag: log_printf(out, PLVL_WARN, "Peer sends ORIGIN_TAG by unicast/broadcast.\n"); break; case pva::pva_app_msg::Search: { uint32_t id; uint8_t flags; pva::SockAddr replyAddr; pva::from_wire(M, id, be); pva::from_wire(M, flags, be); M += 3; // unused/reserved pva::from_wire(M, replyAddr, be); uint16_t port = 0; pva::from_wire(M, port, be); replyAddr.setPort(port); // so far, only "tcp" transport has ever been seen. // however, we will consider and ignore any others which might appear bool foundtcp = false; size_t nproto=0; pva::from_wire(M, pva::Size(nproto), be); for(size_t i=0; i(nchar), be); if(M.size()>=3 && nchar==3 && M[0]=='t' && M[1]=='c' && M[2]=='p') { foundtcp = true; M += 3; break; } } if(!foundtcp && !M.err) { // so far, not something which should actually happen log_printf(out, PLVL_DEBUG, " Search w/o proto \"tcp\"\n"); continue; } // one Search message can include many PV names. uint16_t nchan=0; pva::from_wire(M, nchan, be); for(size_t i=0; i(chlen), be); if(opts.client && chlen<=M.size() && !M.err) { std::string pvname(reinterpret_cast(M.pos), chlen); if(opts.pvnames.empty() || opts.pvnames.find(pvname)!=opts.pvnames.end()) { lazypeer(); log_printf(out, PLVL_INFO, " Search 0x%08x '%s' (rsvp %s)\n", unsigned(id), pvname.c_str(), replyAddr.tostring().c_str()); } } M += chlen; } break; } case pva::pva_app_msg::Beacon: { uint8_t guid[12] = {}; uint8_t seq =0; pva::SockAddr addr; uint16_t port = 0; pva::_from_wire(M, guid, false); M += 1; // flags/qos. unused pva::from_wire(M, seq, be); M += 2; // "change" count. unused pva::from_wire(M, addr, be); pva::from_wire(M, port, be); addr.setPort(port); size_t protolen=0; pva::from_wire(M, pva::Size(protolen), be); M += protolen; // ignore string // ignore remaining "server status" blob if(opts.server && !M.err) { lazypeer(); log_printf(out, PLVL_INFO, " Beacon %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x %s seq %u\n", guid[0], guid[1], guid[2], guid[3], guid[4], guid[5], guid[6], guid[7], guid[8], guid[9], guid[10], guid[11], addr.tostring().c_str(), seq); } } break; default: log_printf(out, PLVL_WARN, "unknown command 0x%02x\n", cmd); } if(M.err) { log_printf(out, PLVL_ERR, " Error while decoding\n"); } } }; std::vector> listeners; listeners.reserve(bindaddrs.size()); for(auto& baddr : bindaddrs) { listeners.push_back(pva::UDPManager::instance() .subscribe(baddr, cb)); log_printf(out, PLVL_DEBUG, "Bind: %s\n", baddr.tostring().c_str()); } #ifdef USE_SIGNAL signal(SIGINT, alldone); signal(SIGTERM, alldone); signal(SIGQUIT, alldone); #endif done.wait(); log_printf(out, PLVL_INFO, "Done\n"); errlogFlush(); return 0; }catch(std::runtime_error& e) { errlogFlush(); std::cerr<<"Error: "<