#include #include #include #include #if !defined(_WIN32) #include #define USE_SIGNAL #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "server.h" #include "pva2pva.h" namespace pvd = epics::pvData; namespace pva = epics::pvAccess; extern int p2pReadOnly; namespace { pvd::StructureConstPtr schema(pvd::getFieldCreate()->createFieldBuilder() ->add("version", pvd::pvUInt) ->add("readOnly", pvd::pvBoolean) ->addNestedStructureArray("clients") ->add("name", pvd::pvString) ->add("provider", pvd::pvString) ->add("addrlist", pvd::pvString) ->add("autoaddrlist", pvd::pvBoolean) ->add("serverport", pvd::pvUShort) ->add("bcastport", pvd::pvUShort) ->endNested() ->addNestedStructureArray("servers") ->add("name", pvd::pvString) ->addArray("clients", pvd::pvString) ->add("interface", pvd::pvString) ->add("addrlist", pvd::pvString) ->add("autoaddrlist", pvd::pvBoolean) ->add("serverport", pvd::pvUShort) ->add("bcastport", pvd::pvUShort) ->add("control_prefix", pvd::pvString) ->endNested() ->createStructure()); void usage(const char *me) { std::cerr<<"Usage: "<\n"; } void getargs(ServerConfig& arg, int argc, char *argv[]) { int opt; bool checkonly = false; while( (opt=getopt(argc, argv, "qvhiIC"))!=-1) { switch(opt) { case 'q': arg.debug--; break; case 'v': arg.debug++; break; case 'I': arg.interactive = true; break; case 'i': arg.interactive = false; break; case 'C': checkonly = true; break; default: std::cerr<<"Unknown argument -"<createPVStructure(schema); std::ifstream strm(argv[optind]); pvd::parseJSON(strm, arg.conf); p2pReadOnly = arg.conf->getSubFieldT("readOnly")->getAs(); unsigned version = arg.conf->getSubFieldT("version")->get(); if(version==0) { std::cerr<<"Warning: config file missing \"version\" key. Assuming 1\n"; } else if(version!=1) { std::cerr<<"config file version mis-match. expect 1 found "<getSubFieldT("clients")->view().empty()) { std::cerr<<"No clients configured\n"; exit(1); } if(arg.conf->getSubFieldT("servers")->view().empty()) { std::cerr<<"No servers configured\n"; exit(1); } if(checkonly) { std::cerr<<"Config file OK\n"; exit(0); } } GWServerChannelProvider::shared_pointer configure_client(ServerConfig& arg, const pvd::PVStructurePtr& conf) { std::string name(conf->getSubFieldT("name")->get()); std::string provider(conf->getSubFieldT("provider")->get()); LOG(pva::logLevelInfo, "Configure client '%s' with provider '%s'", name.c_str(), provider.c_str()); pva::Configuration::shared_pointer C(pva::ConfigurationBuilder() .add("EPICS_PVA_ADDR_LIST", conf->getSubFieldT("addrlist")->get()) .add("EPICS_PVA_AUTO_ADDR_LIST", conf->getSubFieldT("autoaddrlist")->getAs()) .add("EPICS_PVA_SERVER_PORT", conf->getSubFieldT("serverport")->getAs()) .add("EPICS_PVA_BROADCAST_PORT", conf->getSubFieldT("bcastport")->getAs()) .add("EPICS_PVA_DEBUG", arg.debug>=5 ? 5 : 0) .push_map() .build()); pva::ChannelProvider::shared_pointer base(pva::ChannelProviderRegistry::clients()->createProvider(provider, C)); if(!base) throw std::runtime_error("Can't create ChannelProvider"); GWServerChannelProvider::shared_pointer ret(new GWServerChannelProvider(base)); return ret; } pva::ServerContext::shared_pointer configure_server(ServerConfig& arg, const pvd::PVStructurePtr& conf) { std::string name(conf->getSubFieldT("name")->get()); LOG(pva::logLevelInfo, "Configure server '%s'", name.c_str()); pva::Configuration::shared_pointer C(pva::ConfigurationBuilder() .add("EPICS_PVAS_INTF_ADDR_LIST", conf->getSubFieldT("interface")->get()) .add("EPICS_PVAS_BEACON_ADDR_LIST", conf->getSubFieldT("addrlist")->get()) .add("EPICS_PVAS_AUTO_BEACON_ADDR_LIST", conf->getSubFieldT("autoaddrlist")->getAs()) .add("EPICS_PVAS_SERVER_PORT", conf->getSubFieldT("serverport")->getAs()) .add("EPICS_PVAS_BROADCAST_PORT", conf->getSubFieldT("bcastport")->getAs()) .add("EPICS_PVA_DEBUG", arg.debug>=5 ? 5 : 0) .push_map() .build()); pvd::PVStringArray::shared_pointer clients(conf->getSubFieldT("clients")); pvd::PVStringArray::const_svector names(clients->view()); std::vector providers; for(pvd::PVStringArray::const_svector::const_iterator it(names.begin()), end(names.end()); it!=end; ++it) { ServerConfig::clients_t::const_iterator it2(arg.clients.find(*it)); if(it2==arg.clients.end()) throw std::runtime_error("Server references non-existant client"); providers.push_back(it2->second); } pva::ServerContext::shared_pointer ret(pva::ServerContext::create(pva::ServerContext::Config() .config(C) .providers(providers))); return ret; } volatile int quit; epicsEvent done; #ifdef USE_SIGNAL void sigdone(int num) { (void)num; quit = 1; done.signal(); } #endif ServerConfig* volatile theserver; void iocsh_drop(const char *client, const char *channel) { if(!theserver) return; try { theserver->drop(client, channel); }catch(std::exception& e){ std::cout<<"Error: "<status_server(lvl, server); }catch(std::exception& e){ std::cout<<"Error: "<status_client(lvl, client, channel); }catch(std::exception& e){ std::cout<<"Error: "<("drop", "client", "channel"); epics::iocshRegister("gwsr", "level", "channel"); epics::iocshRegister("gwcr", "level", "client", "channel"); libComRegister(); registerReadOnly(); epics::registerRefCounter("ChannelCacheEntry", &ChannelCacheEntry::num_instances); epics::registerRefCounter("ChannelCacheEntry::CRequester", &ChannelCacheEntry::CRequester::num_instances); epics::registerRefCounter("GWChannel", &GWChannel::num_instances); epics::registerRefCounter("MonitorCacheEntry", &MonitorCacheEntry::num_instances); epics::registerRefCounter("MonitorUser", &MonitorUser::num_instances); ServerConfig arg; theserver = &arg; getargs(arg, argc, argv); if(arg.debug>0) std::cout<<"Notice: This p2p gateway prototype has been superceded by the p4p.gw gateway\n" " which has exciting new features including granular access control,\n" " and status PVs including bandwidth usage reports.\n" " p2p is considered deprecated by its author, and will receive\n" " minimal maintainance effort going forward.\n" " Users are encouraged to migrate to p4p.gw.\n" "\n" " https://mdavidsaver.github.io/p4p/gw.html\n" "\n"; pva::pvAccessLogLevel lvl; if(arg.debug<0) lvl = pva::logLevelError; else if(arg.debug==0) lvl = pva::logLevelWarn; else if(arg.debug==1) lvl = pva::logLevelInfo; else if(arg.debug==2) lvl = pva::logLevelDebug; else if(arg.debug==3) lvl = pva::logLevelTrace; else if(arg.debug>=4) lvl = pva::logLevelAll; SET_LOG_LEVEL(lvl); pva::ClientFactory::start(); pvd::PVStructureArray::const_svector arr; arr = arg.conf->getSubFieldT("clients")->view(); for(size_t i=0; igetSubFieldT("name")->get()); if(name.empty()) throw std::runtime_error("Client with empty name not allowed"); ServerConfig::clients_t::const_iterator it(arg.clients.find(name)); if(it!=arg.clients.end()) throw std::runtime_error(std::string("Duplicate client name not allowed : ")+name); arg.clients[name] = configure_client(arg, client); } arr = arg.conf->getSubFieldT("servers")->view(); for(size_t i=0; igetSubFieldT("name")->get()); if(name.empty()) throw std::runtime_error("Server with empty name not allowed"); ServerConfig::servers_t::const_iterator it(arg.servers.find(name)); if(it!=arg.servers.end()) throw std::runtime_error(std::string("Duplicate server name not allowed : ")+name); arg.servers[name] = configure_server(arg, server); } int ret = 0; if(arg.interactive) { ret = iocsh(NULL); } else { #ifdef USE_SIGNAL signal(SIGINT, sigdone); signal(SIGTERM, sigdone); signal(SIGQUIT, sigdone); #endif while(!quit) { done.wait(); } } theserver = 0; return ret; }catch(std::exception& e){ std::cerr<<"Fatal Error : "<