40 Commits

Author SHA1 Message Date
Michael Davidsaver b1d28cca39 Update version numbers for release 2020-07-26 13:41:51 -07:00
Michael Davidsaver 50be5b6025 release notes 2020-07-11 14:08:26 -07:00
Michael Davidsaver 8e7a1ad295 checkDISP 2020-07-05 23:06:30 -07:00
Michael Davidsaver e894ae753f create second dbChannel for DBE_PROPERTY for groups 2020-07-05 23:06:30 -07:00
Michael Davidsaver 4559f483bf create second dbChannel for DBE_PROPERTY
Create second channel, and second set of filter states,
for subscription to DBE_PROPERTY.  Accommodate filters
which drop events.
2020-07-02 22:45:58 -07:00
Michael Davidsaver e843db5d27 softMain compat 2020-06-01 19:54:08 -07:00
Michael Davidsaver 8363c870bd adapt softMain as softIocPVA 2020-06-01 19:54:08 -07:00
Michael Davidsaver 9499137bb4 update softMain.cpp from Base circa 7.0.4 2020-06-01 19:54:08 -07:00
Michael Davidsaver c4798dc2bc shareLib.h cleanup 2020-06-01 18:22:08 -07:00
Michael Davidsaver 11eeed9d0c notes 2020-06-01 18:22:08 -07:00
Andrew Johnson 94d1eedc75 Set next development version 2020-05-28 16:26:01 -05:00
Andrew Johnson cb13435d15 Update version numbers for release 2020-05-28 15:43:34 -05:00
Michael Davidsaver cda2222ed5 p2p: deprecation notice 2020-05-19 17:06:28 -07:00
Michael Davidsaver 68708ff530 p2p: Fix EPICS_PVA*_AUTO_ADDR_LIST 2020-05-19 16:48:53 -07:00
Michael Davidsaver 137ecfed56 ci-scripts 2.3.2 (with appveyor) 2020-04-23 15:16:41 -07:00
Michael Davidsaver 29a6f261dc travis use ci-scripts 2019-12-20 12:06:11 -08:00
Michael Davidsaver 1d9fbbea0b don't install check_consist 2019-12-19 18:45:38 -08:00
Michael Davidsaver 536f4dd02f finalize doxygen 2019-11-03 20:08:49 -08:00
Andrew Johnson 21ae754869 Incr version and set development flag after release 2019-10-31 18:00:34 -05:00
Andrew Johnson 3024f9fb0c Clear development flag for 1.2.2 release 2019-10-31 17:58:09 -05:00
Andrew Johnson 58a2159969 Indent sub-bullets by 2 spaces for pandoc gfm 2019-10-31 17:55:59 -05:00
Michael Davidsaver c200dd22f1 doc for 1.2.2 2019-10-31 10:55:12 -07:00
Michael Davidsaver 9c6fb5c539 more WIN32 include order games 2019-09-26 13:46:26 -07:00
Freddie Akeroyd 54a882d844 MSVC is picky about vector access 2019-09-25 13:47:03 -07:00
Andrew Johnson c7a54de469 Move include of osiSock.h below db headers for Windows 2019-09-07 09:43:11 +02:00
Michael Davidsaver c2ee7c9dc4 m_data removal
Try to avoid stepping on this vxWorks landmine.
2019-09-06 14:57:03 -07:00
Ralph Lange 3e59a4b6a8 rtd-ci: add read-the-docs integration 2019-09-06 15:42:44 +02:00
Michael Davidsaver 0e0400022c vx const fixup
std::set iteration really should always be const
as mutation would invalidate the ordering.
2019-09-05 20:02:46 -07:00
Michael Davidsaver bdbf57350b replace shareLib.h with QSRV_API
and hopefully avoid at least some of the maddening
include order bugs so common with sharedLib.h.
2019-09-05 20:01:56 -07:00
Michael Davidsaver 2e58e54e1d Merge branch 'auth'
* auth:
  minor
  re-add missing enum_t
  asLib check against PeerInfo
2019-08-31 19:22:23 -07:00
Michael Davidsaver ddfd746c62 minor 2019-08-31 19:18:56 -07:00
Michael Davidsaver e41269230c re-add missing enum_t 2019-08-13 20:08:40 -07:00
Michael Davidsaver c3b6fc08b8 re-add missing enum_t 2019-08-13 20:07:26 -07:00
Andrew Johnson d70a2ff8c3 Update version number to 1.2.2 DEVELOPMENT 2019-08-13 11:08:34 -05:00
Michael Davidsaver ce39c93201 release notes for 1.2.1 2019-07-24 17:42:17 -07:00
Michael Davidsaver d7314eaef4 asLib check against PeerInfo
Check Put permissions
2019-05-16 18:19:38 -07:00
Michael Davidsaver 5170c2230d missing 'else'
doesn't actually change logic flow, but wasn't intentional.
2019-05-13 14:09:38 -07:00
Andrew Johnson 3ed88f2559 testpvalink: Provide expected values as 64-bit integers 2019-05-10 15:02:08 -05:00
Andrew Johnson 2c533d79ab Finish off conversion of testpvalink to int64 record types 2019-05-09 18:05:24 -05:00
Michael Davidsaver a72bc31a44 fix release doc 2019-03-20 14:58:47 -07:00
51 changed files with 3516 additions and 552 deletions
+102
View File
@@ -0,0 +1,102 @@
# .appveyor.yml for use with EPICS Base ci-scripts
# (see: https://github.com/epics-base/ci-scripts)
# This is YAML - indentation levels are crucial
cache:
- C:\Users\appveyor\.tools
#---------------------------------#
# additional packages #
#---------------------------------#
install:
# for the sequencer
- cinst re2c
- cmd: git submodule update --init --recursive
#---------------------------------#
# repository cloning #
#---------------------------------#
init:
# Set autocrlf to make batch files work
- git config --global core.autocrlf true
clone_depth: 50
# Skipping commits affecting only specific files
skip_commits:
files:
- 'documentation/*'
- '**/*.md'
# Build Configurations: dll/static, regular/debug
configuration:
- dynamic
- static
- dynamic-debug
- static-debug
# Environment variables: compiler toolchain, base version, setup file, ...
environment:
# common / default variables for all jobs
SETUP_PATH: .ci-local:.ci
matrix:
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
CMP: vs2019
BASE: 7.0
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
CMP: mingw
BASE: 7.0
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
CMP: vs2017
BASE: 7.0
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
CMP: vs2019
BASE: 3.15
# Platform: processor architecture
platform:
- x64
#---------------------------------#
# building & testing #
#---------------------------------#
build_script:
- cmd: python .ci/appveyor/do.py prepare
- cmd: python .ci/appveyor/do.py build
test_script:
- cmd: python .ci/appveyor/do.py test
on_finish:
- ps: Get-ChildItem *.tap -Recurse -Force | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
- cmd: python .ci/appveyor/do.py build test-results -s
#---------------------------------#
# debugging #
#---------------------------------#
## if you want to connect by remote desktop to a failed build, uncomment these lines
## note that you will need to connect within the usual build timeout limit (60 minutes)
## so you may want to adjust the build matrix above to just build the one of interest
#on_failure:
# - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
#---------------------------------#
# notifications #
#---------------------------------#
notifications:
- provider: Email
to:
- me@example.com
on_build_success: false
- provider: GitHubPullRequest
Submodule
+1
Submodule .ci added at ecb7e43660
+14
View File
@@ -0,0 +1,14 @@
# EPICS Base
BASE_DIRNAME=base
BASE_REPONAME=epics-base
BASE_REPOOWNER=epics-base
BASE_VARNAME=EPICS_BASE
BASE_RECURSIVE=NO
MODULES=PVDATA PVACCESS
PVDATA_REPONAME=pvDataCPP
PVDATA_REPOOWNER=epics-base
PVACCESS_REPONAME=pvAccessCPP
PVACCESS_REPOOWNER=epics-base
-10
View File
@@ -1,10 +0,0 @@
#!/bin/sh
set -e -x
make -j2 $EXTRA
if [ "$TEST" != "NO" ]
then
make tapfiles
make -s test-results
fi
-112
View File
@@ -1,112 +0,0 @@
#!/bin/sh
set -e -x
CURDIR="$PWD"
cat << EOF > $CURDIR/configure/RELEASE.local
EPICS_BASE=$HOME/.source/epics-base
EOF
install -d "$HOME/.source"
cd "$HOME/.source"
add_gh_flat() {
MODULE=$1
REPOOWNER=$2
REPONAME=$3
BRANCH=$4
MODULE_UC=$(echo $MODULE | tr 'a-z' 'A-Z')
( git clone --quiet --depth 5 --branch $BRANCH https://github.com/$REPOOWNER/$REPONAME.git $MODULE && \
cd $MODULE && git log -n1 )
cat < $CURDIR/configure/RELEASE.local > $MODULE/configure/RELEASE.local
cat << EOF >> $CURDIR/configure/RELEASE.local
${MODULE_UC}=$HOME/.source/$MODULE
EOF
}
# not recursive
git clone --quiet --depth 5 --branch "$BRBASE" https://github.com/${REPOBASE:-epics-base}/epics-base.git epics-base
(cd epics-base && git log -n1 )
add_gh_flat pvData ${REPOPVD:-epics-base} pvDataCPP ${BRPVD:-master}
add_gh_flat pvAccess ${REPOPVA:-epics-base} pvAccessCPP ${BRPVA:-master}
if [ -e $CURDIR/configure/RELEASE.local ]
then
cat $CURDIR/configure/RELEASE.local
fi
EPICS_HOST_ARCH=`sh epics-base/startup/EpicsHostArch`
# requires wine and g++-mingw-w64-i686
if [ "$WINE" = "32" ]
then
echo "Cross mingw32"
sed -i -e '/CMPLR_PREFIX/d' epics-base/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw
cat << EOF >> epics-base/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw
CMPLR_PREFIX=i686-w64-mingw32-
EOF
cat << EOF >> epics-base/configure/CONFIG_SITE
CROSS_COMPILER_TARGET_ARCHS+=win32-x86-mingw
EOF
fi
if [ "$STATIC" = "YES" ]
then
echo "Build static libraries/executables"
cat << EOF >> epics-base/configure/CONFIG_SITE
SHARED_LIBRARIES=NO
STATIC_BUILD=YES
EOF
fi
case "$CMPLR" in
clang)
echo "Host compiler is clang"
cat << EOF >> epics-base/configure/os/CONFIG_SITE.Common.$EPICS_HOST_ARCH
GNU = NO
CMPLR_CLASS = clang
CC = clang
CCC = clang++
EOF
# hack
sed -i -e 's/CMPLR_CLASS = gcc/CMPLR_CLASS = clang/' epics-base/configure/CONFIG.gnuCommon
clang --version
;;
*)
echo "Host compiler is default"
gcc --version
;;
esac
cat <<EOF >> epics-base/configure/CONFIG_SITE
USR_CPPFLAGS += $USR_CPPFLAGS
USR_CFLAGS += $USR_CFLAGS
USR_CXXFLAGS += $USR_CXXFLAGS
EOF
# set RTEMS to eg. "4.9" or "4.10"
# requires qemu, bison, flex, texinfo, install-info
if [ -n "$RTEMS" ]
then
echo "Cross RTEMS${RTEMS} for pc386"
curl -L "https://github.com/mdavidsaver/rsb/releases/download/20171203-${RTEMS}/i386-rtems${RTEMS}-trusty-20171203-${RTEMS}.tar.bz2" \
| tar -C / -xmj
sed -i -e '/^RTEMS_VERSION/d' -e '/^RTEMS_BASE/d' epics-base/configure/os/CONFIG_SITE.Common.RTEMS
cat << EOF >> epics-base/configure/os/CONFIG_SITE.Common.RTEMS
RTEMS_VERSION=$RTEMS
RTEMS_BASE=$HOME/.rtems
EOF
cat << EOF >> epics-base/configure/CONFIG_SITE
CROSS_COMPILER_TARGET_ARCHS += RTEMS-pc386-qemu
EOF
fi
make -j2 -C epics-base $EXTRA
make -j2 -C pvData $EXTRA
make -j2 -C pvAccess $EXTRA
find epics-base/include
find epics-base/lib
+3
View File
@@ -0,0 +1,3 @@
[submodule ".ci"]
path = .ci
url = https://github.com/epics-base/ci-scripts
+17
View File
@@ -0,0 +1,17 @@
# .readthedocs.yml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Build documentation in the documentation/ directory with Sphinx
sphinx:
configuration: documentation/conf.py
# Build documentation with MkDocs
#mkdocs:
# configuration: mkdocs.yml
# Optionally build your docs in additional formats such as PDF and ePub
formats: all
+97 -19
View File
@@ -1,30 +1,108 @@
sudo: false
dist: trusty
language: c++
compiler:
- gcc
# .travis.yml for use with EPICS Base ci-scripts
# (see: https://github.com/epics-base/ci-scripts)
# This is YAML - indentation levels are crucial
language: cpp
compiler: gcc
dist: bionic
cache:
directories:
- $HOME/.cache
env:
global:
- SETUP_PATH=.ci-local:.ci
addons:
apt:
packages:
# for all EPICS builds
- libreadline6-dev
- libncurses5-dev
- perl
# for clang compiler
- clang
# for mingw builds (32bit and 64bit)
- g++-mingw-w64-i686
- g++-mingw-w64-x86-64
# for RTEMS cross builds
- qemu-system-x86
install:
- ./.ci/travis-prepare.sh
- ./.ci/travis/prepare.sh
script:
- ./.ci/travis-build.sh
env:
- BRBASE=7.0
- BRBASE=7.0 CMPLR=clang
- BRBASE=7.0 EXTRA="CMD_CXXFLAGS=-std=c++98"
- BRBASE=7.0 EXTRA="CMD_CXXFLAGS=-std=c++11"
- BRBASE=7.0 CMPLR=clang EXTRA="CMD_CXXFLAGS=-std=c++11"
- BRBASE=7.0 WINE=32 TEST=NO STATIC=YES
- BRBASE=7.0 WINE=32 TEST=NO STATIC=NO
- BRBASE=7.0 RTEMS=4.10 TEST=NO
- BRBASE=7.0 RTEMS=4.9 TEST=NO
- BRBASE=3.16
- BRBASE=3.15
- ./.ci/travis/build.sh
# If you need to do more during install and build,
# add a local directory to your module and do e.g.
# - ./.ci-local/travis/install-extras.sh
# Define build jobs
# Well-known variables to use
# SET source setup file
# EXTRA content will be added to make command line
# STATIC set to YES for static build (default: NO)
# TEST set to NO to skip running the tests (default: YES)
# VV set to make build scripts verbose (default: unset)
# Usually from setup files, but may be specified or overridden
# on a job line
# MODULES list of dependency modules
# BASE branch or release tag name of the EPICS Base to use
# <MODULE> branch or release tag for a specific module
# ... see README for setup file syntax description
jobs:
include:
# Different configurations of default gcc and clang
- env: BASE=7.0
- env: BASE=7.0
compiler: clang
- env: BASE=7.0 EXTRA="CMD_CXXFLAGS=-std=c++11"
- env: BASE=7.0 EXTRA="CMD_CXXFLAGS=-std=c++11"
compiler: clang
# Trusty: compiler versions very close to RHEL 7
- env: BASE=7.0
dist: trusty
- env: BASE=7.0 EXTRA="CMD_CXXFLAGS=-std=c++11"
dist: trusty
- env: BASE=3.15 EXTRA="CMD_CXXFLAGS=-std=c++11"
dist: trusty
# Cross-compilations to Windows using MinGW and WINE
- env: BASE=7.0 WINE=32 TEST=NO STATIC=YES
compiler: mingw
- env: BASE=7.0 WINE=64 TEST=NO STATIC=NO
compiler: mingw
# Other gcc versions (added as an extra package)
- env: BASE=7.0
compiler: gcc-6
addons: { apt: { packages: ["g++-6"], sources: ["ubuntu-toolchain-r-test"] } }
- env: BASE=7.0
compiler: gcc-7
addons: { apt: { packages: ["g++-7"], sources: ["ubuntu-toolchain-r-test"] } }
# MacOS build
- env: BASE=7.0
os: osx
compiler: clang
addons: { homebrew: { packages: ["re2c"], update: true } }
+2497
View File
File diff suppressed because it is too large Load Diff
-2
View File
@@ -16,8 +16,6 @@
#include "weakmap.h"
#include "weakset.h"
#include <shareLib.h>
struct TestPV;
struct TestPVChannel;
struct TestPVMonitor;
+8 -2
View File
@@ -1,9 +1,15 @@
# Module (source) version
EPICS_QSRV_MAJOR_VERSION = 1
EPICS_QSRV_MINOR_VERSION = 2
EPICS_QSRV_MAINTENANCE_VERSION = 0
EPICS_QSRV_DEVELOPMENT_FLAG = 0
EPICS_QSRV_MAINTENANCE_VERSION = 4
# ABI version
EPICS_QSRV_ABI_MAJOR_VERSION = 1
EPICS_QSRV_ABI_MINOR_VERSION = 1
# Development flag, set to zero for release versions
EPICS_QSRV_DEVELOPMENT_FLAG = 0
# Immediately after a release the MAINTENANCE_VERSION
# will be incremented and the DEVELOPMENT_FLAG set to 1
+1 -1
View File
@@ -38,7 +38,7 @@ PROJECT_NAME = pva2pva
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 1.0.1-DEV
PROJECT_NUMBER = 1.2.4
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
+7
View File
@@ -0,0 +1,7 @@
.wy-side-nav-search {
background-color: #18334B;
}
.wy-side-nav-search input[type="text"] {
border-color: #18334b;
}
+78
View File
@@ -0,0 +1,78 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# http://www.sphinx-doc.org/en/master/config
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- Project information -----------------------------------------------------
project = 'EPICS Documentation'
copyright = '2019, EPICS Controls.'
author = 'EPICS'
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.intersphinx',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# Intersphinx links to subprojects
intersphinx_mapping = {
'how-tos': ('https://docs.epics-controls.org/projects/how-tos/en/latest', None),
}
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_css_files = [
'css/custom.css',
]
master_doc = 'index'
html_theme_options = {
'logo_only': True,
}
html_logo = "images/EPICS_white_logo_v02.png"
html_extra_path = ['../html']
# -- Run Doxygen ------------------------------------------------------------
import subprocess
subprocess.call('cd ..; mkdir -p html/doxygen; doxygen', shell=True)
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

+16
View File
@@ -0,0 +1,16 @@
pva2pva (QSRV / pvAccess Gateway)
=================================
.. toctree::
:hidden:
EPICS Website <https://epics-controls.org>
EPICS Documentation Home <https://docs.epics-controls.org>
.. toctree::
:maxdepth: 1
:caption: pva2pva
Reference Manual and API Documentation <https://docs.epics-controls.org/projects/pva2pva/en/latest/doxygen>
Source Code Repository on GitHub <https://github.com/epics-base/pva2pva>
+2 -1
View File
@@ -11,7 +11,8 @@ runs inside an EPICS IOC process and allows clients
to make requests to access the Process Variables (PVs)
within.
Documentation of @ref qsrv_config including @ref qsrv_group_def
Documentation of @ref qsrv_config including @ref qsrv_group_def ,
@ref qsrv_aslib
and @ref qsrv_link configuration.
- @ref release_notes
+27 -2
View File
@@ -162,6 +162,33 @@ record(ai, "...") {
}
@endcode
@subsection qsrv_aslib Access Security
QSRV will enforce an optional access control policy file (.acf) loaded by the usual means (cf. asSetFilename() ).
This policy is applied to both Single and Group PVs. With Group PVs, restrictions are not defined for the group,
but rather for the individual member records. The same policy will be applied regardess of how a record
is accessed (individually, or through a group).
Policy application differs from CA (RSRV) in several ways:
* Client hostname is always the numeric IP address. HAG() entries must either contained numeric IP addresses,
or that asCheckClientIP=1 flag must be set to translate hostnames into IPs on ACF file load (effects CA server as well).
This prevents clients from trivially forging "hostname".
* In additional to client usernames. UAG definitions may contained items beginning with "role/" which are matched
against the list of groups of which the client username is a member. Username to group lookup is done internally
to QSRV, and depends on IOC host authentication configuration. Note that this is still based on the client provided
username string.
@code
UAG(special) {
someone, "role/op"
}
@endcode
The "special" UAG will match CA or PVA clients with the username "someone".
It will also match a PVA client if the client provided username is a member
of the "op" group (supported on POSIX targets and Windows).
@subsection qsrv_link PVAccess Links
When built against Base >= 3.16.1, support is enabled for PVAccess links,
@@ -170,8 +197,6 @@ for PVA links is quite different.
@note The "dbjlr" and "dbpvar" IOC shell command provide information about PVA links in a running IOC.
@warning The PVA Link syntax shown below is provisional and subject to change.
A simple configuration using defaults is
@code
+40 -13
View File
@@ -2,33 +2,60 @@
@page release_notes Release Notes
Release 1.2.4 (July 2020)
=========================
- Bug Fixes
- Fix stalled monitor when server side filter drops initial DBE_PROPERTY update.
- Respect DISP
- Changes
- Refreshed softIocPVA to match options in Base.
Release 1.2.3 (May 2020)
========================
- P2P gateway deprecated in favor of https://mdavidsaver.github.io/p4p/gw.html
Release 1.2.2 (Nov 2019)
========================
- Changes
- Enforce Access Security policy.
- NTEnum .value field add missing "enum_t" type ID string.
Release 1.2.1 (July 2019)
=========================
- Fixes
- Fix unittest: testpvalink. No functional change.
Release 1.2.0 (Mar 2019)
========================
- Incompatible changes
- The field 'display.format' is replaced with 'display.form' and 'display.precision'.
https://github.com/epics-base/pva2pva/issues/19
- The field 'display.format' is replaced with 'display.form' and 'display.precision'.
https://github.com/epics-base/pva2pva/issues/19
- Additions
- Use @code info(Q:form, "...") @endcode to set 'display.form'. See @ref qsrv_form
- Use @code info(Q:form, "...") @endcode to set 'display.form'. See @ref qsrv_form
- Fixes
- Correct handling of 64-bit integer fields.
- Install a copy of softIocExit.db for standalone builds
- Correct handling of 64-bit integer fields.
- Install a copy of softIocExit.db for standalone builds
Release 1.1.0 (Nov 2018)
==========================
- Incompatible changes
- Requires pvDataCPP >= 7.1.0
- Requires pvAccessCPP >= 6.1.0
- Requires pvDataCPP >= 7.1.0
- Requires pvAccessCPP >= 6.1.0
- Removals
- Drop the broken ioccircle and ioccircle2 examples.
- Drop the broken ioccircle and ioccircle2 examples.
- Fixes
- Fix QSRV monitor locking causing crash
- Fix Windows DLL import/export errors
- Correctly handle empty "scalar" case of NELM=1, NORD=0.
- Fix QSRV monitor locking causing crash
- Fix Windows DLL import/export errors
- Correctly handle empty "scalar" case of NELM=1, NORD=0.
- Additions
- QSRV implement channelList() (aka. 'pvlist') with list of record and group names.
- @ref qsrv_link type (requires Base >= 3.16.1)
- QSRV implement channelList() (aka. 'pvlist') with list of record and group names.
- @ref qsrv_link type (requires Base >= 3.16.1)
Release 1.0.0 (Dec 2017)
========================
-2
View File
@@ -14,8 +14,6 @@
#include "weakmap.h"
#include "weakset.h"
#include <shareLib.h>
struct ChannelCache;
struct ChannelCacheEntry;
struct MonitorUser;
-2
View File
@@ -3,8 +3,6 @@
#include <pv/pvAccess.h>
#include <shareLib.h>
#include "chancache.h"
struct GWChannel : public epics::pvAccess::Channel
+13 -2
View File
@@ -139,7 +139,7 @@ GWServerChannelProvider::shared_pointer configure_client(ServerConfig& arg, cons
pva::Configuration::shared_pointer C(pva::ConfigurationBuilder()
.add("EPICS_PVA_ADDR_LIST", conf->getSubFieldT<pvd::PVString>("addrlist")->get())
.add("EPICS_PVA_AUTO_ADDR_LIST", conf->getSubFieldT<pvd::PVBoolean>("autoaddrlist")->get())
.add("EPICS_PVA_AUTO_ADDR_LIST", conf->getSubFieldT<pvd::PVScalar>("autoaddrlist")->getAs<std::string>())
.add("EPICS_PVA_SERVER_PORT", conf->getSubFieldT<pvd::PVScalar>("serverport")->getAs<pvd::uint16>())
.add("EPICS_PVA_BROADCAST_PORT", conf->getSubFieldT<pvd::PVScalar>("bcastport")->getAs<pvd::uint16>())
.add("EPICS_PVA_DEBUG", arg.debug>=5 ? 5 : 0)
@@ -163,7 +163,7 @@ pva::ServerContext::shared_pointer configure_server(ServerConfig& arg, const pvd
pva::Configuration::shared_pointer C(pva::ConfigurationBuilder()
.add("EPICS_PVAS_INTF_ADDR_LIST", conf->getSubFieldT<pvd::PVString>("interface")->get())
.add("EPICS_PVAS_BEACON_ADDR_LIST", conf->getSubFieldT<pvd::PVString>("addrlist")->get())
.add("EPICS_PVAS_AUTO_BEACON_ADDR_LIST", conf->getSubFieldT<pvd::PVBoolean>("autoaddrlist")->get())
.add("EPICS_PVAS_AUTO_BEACON_ADDR_LIST", conf->getSubFieldT<pvd::PVScalar>("autoaddrlist")->getAs<std::string>())
.add("EPICS_PVAS_SERVER_PORT", conf->getSubFieldT<pvd::PVScalar>("serverport")->getAs<pvd::uint16>())
.add("EPICS_PVAS_BROADCAST_PORT", conf->getSubFieldT<pvd::PVScalar>("bcastport")->getAs<pvd::uint16>())
.add("EPICS_PVA_DEBUG", arg.debug>=5 ? 5 : 0)
@@ -258,6 +258,17 @@ int main(int argc, char *argv[])
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;
-2
View File
@@ -5,8 +5,6 @@
#include <pv/pvAccess.h>
#include <shareLib.h>
typedef epicsGuard<epicsMutex> Guard;
typedef epicsGuardRelease<epicsMutex> UnGuard;
-2
View File
@@ -6,8 +6,6 @@
#include "chancache.h"
#include "channel.h"
#include <shareLib.h>
struct GWServerChannelProvider :
public epics::pvAccess::ChannelProvider,
public epics::pvAccess::ChannelFind,
+25 -25
View File
@@ -75,7 +75,7 @@ private:
mutex_type mutex;
store_t store;
};
std::tr1::shared_ptr<data> m_data;
std::tr1::shared_ptr<data> _data;
struct dtor {
std::tr1::weak_ptr<data> container;
@@ -110,7 +110,7 @@ private:
};
public:
//! Construct a new empty set
weak_value_map() :m_data(new data) {}
weak_value_map() :_data(new data) {}
private:
//! Not copyable
@@ -122,22 +122,22 @@ public:
//! exchange the two sets.
//! @warning Not thread safe (exchanges mutexes as well)
void swap(weak_value_map& O) {
m_data.swap(O.m_data);
_data.swap(O._data);
}
//! Remove all (weak) entries from the set
//! @note Thread safe
void clear() {
guard_type G(m_data->mutex);
return m_data->store.clear();
guard_type G(_data->mutex);
return _data->store.clear();
}
//! Test if set is empty at this moment
//! @note Thread safe
//! @warning see size()
bool empty() const {
guard_type G(m_data->mutex);
return m_data->store.empty();
guard_type G(_data->mutex);
return _data->store.empty();
}
//! number of entries in the set at this moment
@@ -145,8 +145,8 @@ public:
//! @warning May be momentarily inaccurate (larger) due to dead refs.
//! which have not yet been removed.
size_t size() const {
guard_type G(m_data->mutex);
return m_data->store.size();
guard_type G(_data->mutex);
return _data->store.size();
}
//! proxy class for lookup of non-const
@@ -167,8 +167,8 @@ public:
{
if(!v.unique())
throw std::invalid_argument("Only unique() references may be inserted");
value_pointer chainptr(v.get(), dtor(M.m_data, k, v));
M.m_data->store[k] = chainptr;
value_pointer chainptr(v.get(), dtor(M._data, k, v));
M._data->store[k] = chainptr;
v.swap(chainptr);
return v;
}
@@ -215,9 +215,9 @@ public:
value_pointer find(const K& k) const
{
value_pointer ret;
guard_type G(m_data->mutex);
typename store_t::const_iterator it(m_data->store.find(k));
if(it!=m_data->store.end()) {
guard_type G(_data->mutex);
typename store_t::const_iterator it(_data->store.find(k));
if(it!=_data->store.end()) {
// may be nullptr if we race destruction
// as ref. count falls to zero before we can remove it
ret = it->second.lock();
@@ -230,9 +230,9 @@ public:
value_pointer insert(const K& k, value_pointer& v)
{
value_pointer ret;
guard_type G(m_data->mutex);
typename store_t::const_iterator it = m_data->store.find(k);
if(it!=m_data->store.end())
guard_type G(_data->mutex);
typename store_t::const_iterator it = _data->store.find(k);
if(it!=_data->store.end())
ret = it->second.lock();
(*this)[k] = v;
return ret;
@@ -243,9 +243,9 @@ public:
lock_map_type lock_map() const
{
lock_map_type ret;
guard_type G(m_data->mutex);
for(typename store_t::const_iterator it = m_data->store.begin(),
end = m_data->store.end(); it!=end; ++it)
guard_type G(_data->mutex);
for(typename store_t::const_iterator it = _data->store.begin(),
end = _data->store.end(); it!=end; ++it)
{
value_pointer P(it->second.lock);
if(P) ret[it->first] = P;
@@ -259,10 +259,10 @@ public:
lock_vector_type lock_vector() const
{
lock_vector_type ret;
guard_type G(m_data->mutex);
ret.reserve(m_data->store.size());
for(typename store_t::const_iterator it = m_data->store.begin(),
end = m_data->store.end(); it!=end; ++it)
guard_type G(_data->mutex);
ret.reserve(_data->store.size());
for(typename store_t::const_iterator it = _data->store.begin(),
end = _data->store.end(); it!=end; ++it)
{
value_pointer P(it->second.lock());
if(P) ret.push_back(std::make_pair(it->first, P));
@@ -274,7 +274,7 @@ public:
//! for use with batch operations.
//! @warning Use caution when swap()ing while holding this lock!
inline epicsMutex& mutex() const {
return m_data->mutex;
return _data->mutex;
}
};
+25 -25
View File
@@ -94,7 +94,7 @@ private:
mutex_type mutex;
store_t store;
};
std::tr1::shared_ptr<data> m_data;
std::tr1::shared_ptr<data> _data;
//! Destroyer for a chained shared_ptr
//! which holds the unique() real strong
@@ -131,7 +131,7 @@ private:
};
public:
//! Construct a new empty set
weak_set() :m_data(new data) {}
weak_set() :_data(new data) {}
private:
//! Not copyable
@@ -143,22 +143,22 @@ public:
//! exchange the two sets.
//! @warning Not thread safe (exchanges mutexes as well)
void swap(weak_set& O) {
m_data.swap(O.m_data);
_data.swap(O._data);
}
//! Remove all (weak) entries from the set
//! @note Thread safe
void clear() {
guard_type G(m_data->mutex);
return m_data->store.clear();
guard_type G(_data->mutex);
return _data->store.clear();
}
//! Test if set is empty
//! @note Thread safe
//! @warning see size()
bool empty() const {
guard_type G(m_data->mutex);
return m_data->store.empty();
guard_type G(_data->mutex);
return _data->store.empty();
}
//! number of entries in the set at this moment
@@ -166,8 +166,8 @@ public:
//! @warning May be momentarily inaccurate (larger) due to dead refs.
//! which have not yet been removed.
size_t size() const {
guard_type G(m_data->mutex);
return m_data->store.size();
guard_type G(_data->mutex);
return _data->store.size();
}
//! Insert a new entry into the set
@@ -178,8 +178,8 @@ public:
//! Remove any (weak) ref to this object from the set
//! @returns the number of objects removed (0 or 1)
size_t erase(value_pointer& v) {
guard_type G(m_data->mutex);
return m_data->store.erase(v);
guard_type G(_data->mutex);
return _data->store.erase(v);
}
//! Return a set of strong references to all entries
@@ -197,7 +197,7 @@ public:
//! for use with batch operations.
//! @warning Use caution when swap()ing while holding this lock!
inline epicsMutex& mutex() const {
return m_data->mutex;
return _data->mutex;
}
//! an iterator-ish object which also locks the set during iteration
@@ -205,7 +205,7 @@ public:
weak_set& set;
epicsGuard<epicsMutex> guard;
typename store_t::iterator it, end;
XIterator(weak_set& S) :set(S), guard(S.mutex()), it(S.m_data->store.begin()), end(S.m_data->store.end()) {}
XIterator(weak_set& S) :set(S), guard(S.mutex()), it(S._data->store.begin()), end(S._data->store.end()) {}
//! yield the next live entry
value_pointer next() {
value_pointer ret;
@@ -229,14 +229,14 @@ void weak_set<T>::insert(value_pointer &v)
if(!v.unique())
throw std::invalid_argument("Only unique() references may be inserted");
guard_type G(m_data->mutex);
typename store_t::const_iterator it = m_data->store.find(v);
if(it==m_data->store.end()) { // new object
guard_type G(_data->mutex);
typename store_t::const_iterator it = _data->store.find(v);
if(it==_data->store.end()) { // new object
// wrapped strong ref. which removes from our map
value_pointer chainptr(v.get(), dtor(m_data, v));
value_pointer chainptr(v.get(), dtor(_data, v));
m_data->store.insert(chainptr);
_data->store.insert(chainptr);
v.swap(chainptr); // we only keep the chained pointer
} else {
@@ -253,9 +253,9 @@ typename weak_set<T>::set_type
weak_set<T>::lock_set() const
{
set_type ret;
guard_type G(m_data->mutex);
for(typename store_t::const_iterator it=m_data->store.begin(),
end=m_data->store.end(); it!=end; ++it)
guard_type G(_data->mutex);
for(typename store_t::const_iterator it=_data->store.begin(),
end=_data->store.end(); it!=end; ++it)
{
value_pointer P(it->lock());
if(P) ret.insert(P);
@@ -275,10 +275,10 @@ weak_set<T>::lock_vector() const
template<typename T>
void weak_set<T>::lock_vector(vector_type& ret) const
{
guard_type G(m_data->mutex);
ret.reserve(m_data->store.size());
for(typename store_t::const_iterator it=m_data->store.begin(),
end=m_data->store.end(); it!=end; ++it)
guard_type G(_data->mutex);
ret.reserve(_data->store.size());
for(typename store_t::const_iterator it=_data->store.begin(),
end=_data->store.end(); it!=end; ++it)
{
value_pointer P(it->lock());
if(P) ret.push_back(P);
+4 -1
View File
@@ -16,6 +16,7 @@ LIBRARY += qsrv
SHRLIB_VERSION ?= $(EPICS_QSRV_ABI_MAJOR_VERSION).$(EPICS_QSRV_ABI_MINOR_VERSION)
USR_CPPFLAGS += -DQSRV_API_BUILDING
USR_CPPFLAGS += -I$(TOP)/common -I$(TOP)/p2pApp
INC += pv/qsrv.h
@@ -59,7 +60,9 @@ qsrv_LIBS += $(EPICS_BASE_IOC_LIBS)
FINAL_LOCATION ?= $(shell $(PERL) $(TOOLS)/fullPathName.pl $(INSTALL_LOCATION))
USR_CPPFLAGS += -DFINAL_LOCATION="\"$(FINAL_LOCATION)\""
# since we copy softMain.cpp from Base, use the same
# EPICS_BASE macro for our location
USR_CPPFLAGS += -DEPICS_BASE="\"$(FINAL_LOCATION)\""
PROD_IOC += softIocPVA
-1
View File
@@ -14,7 +14,6 @@
#include <pv/configuration.h>
#include <pv/json.h>
#define epicsExportSharedSymbols
#include "pdbgroup.h"
namespace {
-1
View File
@@ -13,7 +13,6 @@
#include <pv/pvData.h>
#include <pv/anyscalar.h>
#define epicsExportSharedSymbols
#include "pvif.h"
namespace pvd = epics::pvData;
+8 -2
View File
@@ -19,7 +19,6 @@
#include <pv/pvAccess.h>
#include <pv/configuration.h>
#define epicsExportSharedSymbols
#include "helper.h"
#include "pdbsingle.h"
#include "pvif.h"
@@ -420,6 +419,12 @@ PDBProvider::PDBProvider(const epics::pvAccess::Configuration::const_shared_poin
members_map[mem.pvfldname] = J;
PDBGroupPV::Info& info = members[J];
DBCH chan2;
if(chan.chan && (ellCount(&chan.chan->pre_chain)>0 || ellCount(&chan.chan->post_chain)>0)) {
DBCH temp(mem.pvname);
info.chan2.swap(chan2);
}
info.allowProc = mem.putorder != std::numeric_limits<int>::min();
info.builder = PTRMOVE(mem.builder);
assert(info.builder.get());
@@ -515,7 +520,8 @@ PDBProvider::PDBProvider(const epics::pvAccess::Configuration::const_shared_poin
info.pvif.reset(info.builder->attach(info.chan, pv->complete, info.attachment));
// TODO: don't need evt_PROPERTY for PVIF plain
info.evt_PROPERTY.create(event_context, info.chan, &pdb_group_event, DBE_PROPERTY);
dbChannel *pchan = info.chan2.chan ? info.chan2.chan : info.chan.chan;
info.evt_PROPERTY.create(event_context, pchan, &pdb_group_event, DBE_PROPERTY);
if(!info.triggers.empty()) {
info.evt_VALUE.create(event_context, info.chan, &pdb_group_event, DBE_VALUE|DBE_ALARM);
+3 -3
View File
@@ -8,7 +8,7 @@
#include "weakmap.h"
#include <shareLib.h>
#include <pv/qsrv.h>
struct PDBProvider;
@@ -30,7 +30,7 @@ struct PDBPV
virtual void show(int lvl) {}
};
struct epicsShareClass PDBProvider : public epics::pvAccess::ChannelProvider,
struct QSRV_API PDBProvider : public epics::pvAccess::ChannelProvider,
public epics::pvAccess::ChannelFind,
public std::tr1::enable_shared_from_this<PDBProvider>
{
@@ -67,7 +67,7 @@ struct epicsShareClass PDBProvider : public epics::pvAccess::ChannelProvider,
static size_t num_instances;
};
epicsShareFunc
QSRV_API
void QSRVRegistrar_counters();
#endif // PDB_H
+12 -2
View File
@@ -13,7 +13,6 @@
#include <pv/configuration.h>
#include <pv/epicsException.h>
#define epicsExportSharedSymbols
#include "helper.h"
#include "pdbgroup.h"
#include "pdb.h"
@@ -139,6 +138,15 @@ PDBGroupPV::connect(const std::tr1::shared_ptr<PDBProvider>& prov,
const pva::ChannelRequester::shared_pointer& req)
{
PDBGroupChannel::shared_pointer ret(new PDBGroupChannel(shared_from_this(), prov, req));
ret->cred.update(req);
ret->aspvt.resize(members.size());
for(size_t i=0, N=members.size(); i<N; i++)
{
ret->aspvt[i].add(members[i].chan, ret->cred);
}
return ret;
}
@@ -368,7 +376,9 @@ void PDBGroupPut::put(pvd::PVStructure::shared_pointer const & value,
DBScanLocker L(dbChannelRecord(info.chan));
ret |= putpvif[i]->get(*changed, info.allowProc ? doProc : PVIF::ProcInhibit);
ret |= putpvif[i]->get(*changed,
info.allowProc ? doProc : PVIF::ProcInhibit,
channel->aspvt[i].canWrite());
}
}
+10 -7
View File
@@ -17,11 +17,9 @@
#include "pvif.h"
#include "pdb.h"
#include <shareLib.h>
struct epicsShareClass GroupConfig
struct QSRV_API GroupConfig
{
struct epicsShareClass Field {
struct QSRV_API Field {
std::string type, channel, trigger, id;
int putorder;
@@ -36,7 +34,7 @@ struct epicsShareClass GroupConfig
}
};
struct epicsShareClass Group {
struct QSRV_API Group {
typedef std::map<std::string, Field> fields_t;
fields_t fields;
bool atomic, atomic_set;
@@ -70,7 +68,7 @@ struct PDBGroupMonitor;
void pdb_group_event(void *user_arg, struct dbChannel *chan,
int eventsRemaining, struct db_field_log *pfl);
struct epicsShareClass PDBGroupPV : public PDBPV
struct QSRV_API PDBGroupPV : public PDBPV
{
POINTER_DEFINITIONS(PDBGroupPV);
weak_pointer weakself;
@@ -89,6 +87,8 @@ struct epicsShareClass PDBGroupPV : public PDBPV
struct Info {
DBCH chan;
// used for DBE_PROPERTY subscription when chan has filters
DBCH chan2;
std::tr1::shared_ptr<PVIFBuilder> builder;
FieldName attachment;
typedef std::vector<size_t> triggers_t;
@@ -133,12 +133,15 @@ struct epicsShareClass PDBGroupPV : public PDBPV
virtual void show(int lvl) OVERRIDE;
};
struct epicsShareClass PDBGroupChannel : public BaseChannel,
struct QSRV_API PDBGroupChannel : public BaseChannel,
public std::tr1::enable_shared_from_this<PDBGroupChannel>
{
POINTER_DEFINITIONS(PDBGroupChannel);
PDBGroupPV::shared_pointer pv;
std::vector<ASCLIENT> aspvt;
// storage referenced from aspvt
ASCred cred;
static size_t num_instances;
+29 -7
View File
@@ -5,15 +5,16 @@
#include <dbAccess.h>
#include <dbChannel.h>
#include <dbStaticLib.h>
#include <epicsAtomic.h>
#include <errlog.h>
#include <dbNotify.h>
#include <osiSock.h>
#include <epicsAtomic.h>
#include <pv/epicsException.h>
#include <pv/pvAccess.h>
#include <pv/security.h>
#include <pv/configuration.h>
#define epicsExportSharedSymbols
#include "helper.h"
#include "pdbsingle.h"
#include "pdb.h"
@@ -102,6 +103,10 @@ PDBSinglePV::PDBSinglePV(DBCH& chan,
,hadevent_VALUE(false)
,hadevent_PROPERTY(false)
{
if(ellCount(&chan.chan->pre_chain) || ellCount(&chan.chan->post_chain)) {
DBCH temp(dbChannelName(chan.chan));
this->chan2.swap(temp);
}
this->chan.swap(chan);
fielddesc = std::tr1::static_pointer_cast<const pvd::Structure>(builder->dtype(this->chan));
@@ -119,8 +124,9 @@ PDBSinglePV::~PDBSinglePV()
void PDBSinglePV::activate()
{
dbChannel *pchan = this->chan2.chan ? this->chan2.chan : this->chan.chan;
evt_VALUE.create(provider->event_context, this->chan, &pdb_single_event, DBE_VALUE|DBE_ALARM);
evt_PROPERTY.create(provider->event_context, this->chan, &pdb_single_event, DBE_PROPERTY);
evt_PROPERTY.create(provider->event_context, pchan, &pdb_single_event, DBE_PROPERTY);
}
pva::Channel::shared_pointer
@@ -128,6 +134,11 @@ PDBSinglePV::connect(const std::tr1::shared_ptr<PDBProvider>& prov,
const pva::ChannelRequester::shared_pointer& req)
{
PDBSingleChannel::shared_pointer ret(new PDBSingleChannel(shared_from_this(), req));
ret->cred.update(req);
ret->aspvt.add(chan, ret->cred);
return ret;
}
@@ -145,7 +156,7 @@ void PDBSinglePV::addMonitor(PDBSingleMonitor* mon)
db_post_single_event(evt_VALUE.subscript);
db_post_single_event(evt_PROPERTY.subscript);
} if(hadevent_VALUE && hadevent_PROPERTY) {
} else if(hadevent_VALUE && hadevent_PROPERTY) {
// new subscriber and already had initial update
mon->post(G);
} // else new subscriber, but no initial update. so just wait
@@ -200,7 +211,15 @@ PDBSingleChannel::~PDBSingleChannel()
void PDBSingleChannel::printInfo(std::ostream& out)
{
out<<"PDBSingleChannel";
if(aspvt.canWrite())
out << "RW ";
else
out << "RO ";
out<<(&cred.user[0])<<'@'<<(&cred.host[0]);
for(size_t i=0, N=cred.groups.size(); i<N; i++) {
out<<", "<<(&cred.groups[i][0]);
}
out<<"\n";
}
pva::ChannelPut::shared_pointer
@@ -337,7 +356,10 @@ void PDBSinglePut::put(pvd::PVStructure::shared_pointer const & value,
dbFldDes *fld = dbChannelFldDes(chan);
pvd::Status ret;
if(dbChannelFieldType(chan)>=DBF_INLINK && dbChannelFieldType(chan)<=DBF_FWDLINK) {
if(!channel->aspvt.canWrite()) {
ret = pvd::Status::error("Put not permitted");
} else if(dbChannelFieldType(chan)>=DBF_INLINK && dbChannelFieldType(chan)<=DBF_FWDLINK) {
try{
std::string lval(value->getSubFieldT<pvd::PVScalar>("value")->getAs<std::string>());
long status = dbChannelPutField(chan, DBF_STRING, lval.c_str(), 1);
@@ -378,7 +400,7 @@ void PDBSinglePut::put(pvd::PVStructure::shared_pointer const & value,
p2p::auto_ptr<PVIF> putpvif(channel->pv->builder->attach(channel->pv->chan, value, FieldName()));
try{
DBScanLocker L(chan);
putpvif->get(*changed, doProc);
ret = putpvif->get(*changed, doProc);
}catch(std::runtime_error& e){
ret = pvd::Status::error(e.what());
+7 -3
View File
@@ -5,6 +5,7 @@
#include <dbAccess.h>
#include <dbNotify.h>
#include <asLib.h>
#include <dbEvent.h>
@@ -15,11 +16,9 @@
#include "pvif.h"
#include "pdb.h"
#include <shareLib.h>
struct PDBSingleMonitor;
struct epicsShareClass PDBSinglePV : public PDBPV
struct QSRV_API PDBSinglePV : public PDBPV
{
POINTER_DEFINITIONS(PDBSinglePV);
weak_pointer weakself;
@@ -31,6 +30,8 @@ struct epicsShareClass PDBSinglePV : public PDBPV
* is locked.
*/
DBCH chan;
// used for DBE_PROPERTY subscription when chan has filters
DBCH chan2;
PDBProvider::shared_pointer provider;
// only for use in pdb_single_event()
@@ -78,6 +79,9 @@ struct PDBSingleChannel : public BaseChannel,
POINTER_DEFINITIONS(PDBSingleChannel);
PDBSinglePV::shared_pointer pv;
// storage referenced from aspvt
ASCred cred;
ASCLIENT aspvt;
static size_t num_instances;
+28 -6
View File
@@ -2,7 +2,6 @@
#define PV_QSRV_H
#include <epicsVersion.h>
#include <shareLib.h>
#ifndef VERSION_INT
# define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P))
@@ -15,6 +14,29 @@
#define QSRV_ABI_VERSION_INT VERSION_INT(EPICS_QSRV_ABI_MAJOR_VERSION, EPICS_QSRV_ABI_MINOR_VERSION, 0, 0)
#if defined(QSRV_API_BUILDING) && defined(epicsExportSharedSymbols)
# error Use QSRV_API or shareLib.h not both
#endif
#if defined(_WIN32) || defined(__CYGWIN__)
# if defined(QSRV_API_BUILDING) && defined(EPICS_BUILD_DLL)
/* building library as dll */
# define QSRV_API __declspec(dllexport)
# elif !defined(QSRV_API_BUILDING) && defined(EPICS_CALL_DLL)
/* calling library in dll form */
# define QSRV_API __declspec(dllimport)
# endif
#elif __GNUC__ >= 4
# define QSRV_API __attribute__ ((visibility("default")))
#endif
#ifndef QSRV_API
# define QSRV_API
#endif
#ifdef __cplusplus
extern "C" {
#endif
@@ -22,12 +44,12 @@ extern "C" {
struct link; /* aka. DBLINK from link.h */
/** returns QSRV_VERSION_INT captured at compilation time */
epicsShareExtern unsigned qsrvVersion(void);
QSRV_API unsigned qsrvVersion(void);
/** returns QSRV_ABI_VERSION_INT captured at compilation time */
epicsShareExtern unsigned qsrvABIVersion(void);
QSRV_API unsigned qsrvABIVersion(void);
epicsShareFunc void testqsrvWaitForLinkEvent(struct link *plink);
QSRV_API void testqsrvWaitForLinkEvent(struct link *plink);
/** Call before testIocShutdownOk()
@code
@@ -41,7 +63,7 @@ epicsShareFunc void testqsrvWaitForLinkEvent(struct link *plink);
testdbCleanup();
@endcode
*/
epicsShareExtern void testqsrvShutdownOk(void);
QSRV_API void testqsrvShutdownOk(void);
/** Call after testIocShutdownOk() and before testdbCleanup()
@code
@@ -55,7 +77,7 @@ epicsShareExtern void testqsrvShutdownOk(void);
testdbCleanup();
@endcode
*/
epicsShareExtern void testqsrvCleanup(void);
QSRV_API void testqsrvCleanup(void);
#ifdef __cplusplus
}
+1 -2
View File
@@ -26,13 +26,12 @@
#include <pv/reftrack.h>
#include <pva/client.h>
#include <epicsExport.h> /* defines epicsExportSharedSymbols */
#include "pv/qsrv.h"
#include "helper.h"
#include "pvif.h"
#include "pvalink.h"
#include <epicsExport.h> /* defines epicsExportSharedSymbols */
int pvaLinkDebug;
int pvaLinkIsolate;
+3 -13
View File
@@ -4,11 +4,6 @@
#include <set>
#include <map>
#ifdef epicsExportSharedSymbols
# define pvalinkEpicsExportSharedSymbols
# undef epicsExportSharedSymbols
#endif
#define EPICS_DBCA_PRIVATE_API
#include <epicsGuard.h>
#include <dbAccess.h>
@@ -44,19 +39,14 @@
#include <pv/sharedPtr.h>
#ifdef pvalinkEpicsExportSharedSymbols
# define epicsExportSharedSymbols
# undef pvalinkEpicsExportSharedSymbols
#endif
#include "helper.h"
#include "pvif.h"
#include "tpool.h"
extern "C" {
epicsShareExtern int pvaLinkDebug;
epicsShareExtern int pvaLinkIsolate;
epicsShareExtern int pvaLinkNWorkers;
QSRV_API extern int pvaLinkDebug;
QSRV_API extern int pvaLinkIsolate;
QSRV_API extern int pvaLinkNWorkers;
}
#if 0
-3
View File
@@ -3,9 +3,6 @@
#include <pv/reftrack.h>
#define epicsExportSharedSymbols
#include <shareLib.h>
#include "pvalink.h"
int pvaLinkNWorkers = 1;
-3
View File
@@ -2,9 +2,6 @@
#include <epicsStdio.h> // redirects stdout/stderr
#define epicsExportSharedSymbols
#include <shareLib.h>
#include "pvalink.h"
namespace pvalink {
-3
View File
@@ -1,9 +1,6 @@
#include <pv/reftrack.h>
#include <alarm.h>
#define epicsExportSharedSymbols
#include <shareLib.h>
#include "pvalink.h"
namespace pvalink {
-3
View File
@@ -6,9 +6,6 @@
#include <pv/current_function.h>
#define epicsExportSharedSymbols
#include <shareLib.h>
#include "pvalink.h"
+148 -22
View File
@@ -12,14 +12,16 @@
#include <errSymTbl.h>
#include <epicsVersion.h>
#include <errlog.h>
#include <osiSock.h>
#include <pv/status.h>
#include <pv/bitSet.h>
#include <pv/pvData.h>
#include <pv/anyscalar.h>
#include <pv/reftrack.h>
#include <pv/pvAccess.h>
#include <pv/security.h>
#define epicsExportSharedSymbols
#include "sb.h"
#include "pvif.h"
@@ -34,6 +36,7 @@
#endif
namespace pvd = epics::pvData;
namespace pva = epics::pvAccess;
DBCH::DBCH(dbChannel *ch) :chan(ch)
{
@@ -66,6 +69,97 @@ void DBCH::swap(DBCH& o)
std::swap(chan, o.chan);
}
void ASCred::update(const pva::ChannelRequester::shared_pointer& req)
{
pva::PeerInfo::const_shared_pointer info(req->getPeerInfo());
std::string usertemp, hosttemp;
if(info && info->identified) {
hosttemp = info->peer;
if(info->authority=="ca") {
usertemp = info->account;
size_t sep = usertemp.find_last_of('/');
if(sep != std::string::npos) {
// prevent CA auth from claiming to be eg. "krb/someone.special"
usertemp = usertemp.substr(sep+1);
}
} else {
usertemp = info->authority + "/" + info->account;
}
const char role[] = "role/";
groups.resize(info->roles.size());
size_t idx = 0u;
for(pva::PeerInfo::roles_t::const_iterator it(info->roles.begin()), end(info->roles.end()); it!=end; ++it, idx++) {
groups[idx].resize((*it).size()+sizeof(role)); // sizeof(role) includes trailing nil
std::copy(role,
role+sizeof(role)-1,
groups[idx].begin());
std::copy(it->begin(),
it->end(),
groups[idx].begin()+sizeof(role)-1);
groups[idx][groups[idx].size()-1] = '\0';
}
} else {
// legacy and anonymous
hosttemp = req->getRequesterName();
}
// remote names have the form "IP:port"
size_t sep = hosttemp.find_first_of(':');
if(sep == std::string::npos) {
sep = hosttemp.size();
}
hosttemp.resize(sep);
host.resize(hosttemp.size()+1);
std::copy(hosttemp.begin(),
hosttemp.end(),
host.begin());
host[hosttemp.size()] = '\0';
user.resize(usertemp.size()+1);
std::copy(usertemp.begin(),
usertemp.end(),
user.begin());
user[usertemp.size()] = '\0';
}
ASCLIENT::~ASCLIENT()
{
asRemoveClient(&aspvt);
for(size_t i=0, N=grppvt.size(); i<N; i++) {
asRemoveClient(&grppvt[i]);
}
}
void ASCLIENT::add(dbChannel* chan, ASCred& cred)
{
asRemoveClient(&aspvt);
/* asAddClient() fails secure to no-permission */
(void)asAddClient(&aspvt, dbChannelRecord(chan)->asp, dbChannelFldDes(chan)->as_level, &cred.user[0], &cred.host[0]);
grppvt.resize(cred.groups.size(), 0);
for(size_t i=0, N=grppvt.size(); i<N; i++) {
asRemoveClient(&grppvt[i]);
(void)asAddClient(&grppvt[i], dbChannelRecord(chan)->asp, dbChannelFldDes(chan)->as_level, &cred.groups[i][0], &cred.host[0]);
}
}
bool ASCLIENT::canWrite() {
if(!asActive || (aspvt && asCheckPut(aspvt)))
return true;
for(size_t i=0, N=grppvt.size(); i<N; i++) {
if(grppvt[i] && asCheckPut(grppvt[i]))
return true;
}
return false;
}
PVIF::PVIF(dbChannel *ch)
:chan(ch)
{}
@@ -547,6 +641,14 @@ void findFormat(pvTimeAlarm& pvmeta, pdbRecordIterator& info, const epics::pvDat
}
}
pvd::Status checkDISP(dbChannel *chan)
{
dbCommon *prec = dbChannelRecord(chan);
pvd::Status ret;
if(prec->disp && dbChannelField(chan)!=&prec->disp)
ret = pvd::Status::error("Put Disabled");
return ret;
}
template<typename PVX, typename META>
struct PVIFScalarNumeric : public PVIF
@@ -602,15 +704,24 @@ struct PVIFScalarNumeric : public PVIF
}
}
virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc) OVERRIDE FINAL
virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc, bool permit) OVERRIDE FINAL
{
pvd::Status ret;
pvd::Status ret = checkDISP(chan);
if(!ret)
return ret;
bool newval = mask.logical_and(pvmeta.maskVALUEPut);
if(newval) {
getValue(pvmeta.chan, pvmeta.value.get());
if(permit)
getValue(pvmeta.chan, pvmeta.value.get());
else
ret = pvd::Status::error("Put not permitted");
}
if(newval || proc==PVIF::ProcForce) {
ret = PVIF::get(mask, proc);
if(permit)
ret = PVIF::get(mask, proc);
else
ret = pvd::Status::error("Process not permitted");
}
return ret;
}
@@ -682,6 +793,7 @@ ScalarBuilder::dtype(dbChannel *channel)
if(dbr==DBR_ENUM)
builder = builder->setId("epics:nt/NTEnum:1.0")
->addNestedStructure("value")
->setId("enum_t")
->add("index", pvd::pvInt)
->addArray("choices", pvd::pvString)
->endNested();
@@ -795,7 +907,7 @@ struct PVIFPlain : public PVIF
virtual ~PVIFPlain() {}
virtual void put(epics::pvData::BitSet& mask, unsigned dbe, db_field_log *pfl)
virtual void put(epics::pvData::BitSet& mask, unsigned dbe, db_field_log *pfl) OVERRIDE FINAL
{
if(dbe&DBE_VALUE) {
putValue(channel, field.get(), pfl);
@@ -803,20 +915,29 @@ struct PVIFPlain : public PVIF
}
}
virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc)
virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc, bool permit) OVERRIDE FINAL
{
pvd::Status ret;
pvd::Status ret = checkDISP(chan);
if(!ret)
return ret;
bool newval = mask.get(fieldOffset);
if(newval) {
getValue(channel, field.get());
if(permit)
getValue(channel, field.get());
else
ret = pvd::Status::error("Put not permitted");
}
if(newval || proc==PVIF::ProcForce) {
ret = PVIF::get(mask, proc);
if(permit)
ret = PVIF::get(mask, proc);
else
ret = pvd::Status::error("Process not permitted");
}
return ret;
}
virtual unsigned dbe(const epics::pvData::BitSet& mask)
virtual unsigned dbe(const epics::pvData::BitSet& mask) OVERRIDE FINAL
{
// TODO: figure out how to handle various intermidiate compressed
// bitSet and enclosing.
@@ -940,7 +1061,7 @@ struct PVIFMeta : public PVIF
virtual ~PVIFMeta() {}
virtual void put(epics::pvData::BitSet& mask, unsigned dbe, db_field_log *pfl)
virtual void put(epics::pvData::BitSet& mask, unsigned dbe, db_field_log *pfl) OVERRIDE FINAL
{
mask |= meta.maskALWAYS;
if(dbe&DBE_ALARM)
@@ -949,13 +1070,15 @@ struct PVIFMeta : public PVIF
putTime(meta, dbe, pfl);
}
virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc)
virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc, bool permit) OVERRIDE FINAL
{
// can't put time/alarm
if(mask.logical_and(meta.maskALARM))
return pvd::Status::warn("Put to meta field ignored");
return pvd::Status::Ok;
}
virtual unsigned dbe(const epics::pvData::BitSet& mask)
virtual unsigned dbe(const epics::pvData::BitSet& mask) OVERRIDE FINAL
{
if(mask.logical_and(meta.maskALARM))
return DBE_ALARM;
@@ -1010,18 +1133,18 @@ struct PVIFProc : public PVIF
{
PVIFProc(dbChannel *channel) :PVIF(channel) {}
virtual void put(epics::pvData::BitSet& mask, unsigned dbe, db_field_log *pfl)
virtual void put(epics::pvData::BitSet& mask, unsigned dbe, db_field_log *pfl) OVERRIDE FINAL
{
// nothing to get
}
virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc)
virtual pvd::Status get(const epics::pvData::BitSet& mask, proc_t proc, bool permit) OVERRIDE FINAL
{
// always process
return PVIF::get(mask, PVIF::ProcForce);
// always process (if permitted)
return PVIF::get(mask, PVIF::ProcForce, permit);
}
virtual unsigned dbe(const epics::pvData::BitSet& mask)
virtual unsigned dbe(const epics::pvData::BitSet& mask) OVERRIDE FINAL
{
return 0;
}
@@ -1036,7 +1159,7 @@ struct ProcBuilder : public PVIFBuilder
virtual epics::pvData::FieldBuilderPtr dtype(epics::pvData::FieldBuilderPtr& builder,
const std::string& fld,
dbChannel *channel)
dbChannel *channel) OVERRIDE FINAL
{
// invisible
return builder;
@@ -1054,7 +1177,7 @@ struct ProcBuilder : public PVIFBuilder
}//namespace
pvd::Status PVIF::get(const epics::pvData::BitSet& mask, proc_t proc)
pvd::Status PVIF::get(const epics::pvData::BitSet& mask, proc_t proc, bool permit)
{
dbCommon *precord = dbChannelRecord(chan);
@@ -1066,7 +1189,10 @@ pvd::Status PVIF::get(const epics::pvData::BitSet& mask, proc_t proc)
pvd::Status ret;
if (tryproc) {
if (precord->pact) {
if (!permit) {
return pvd::Status::error("Process not permitted");
} else if (precord->pact) {
if (precord->tpro)
printf("%s: Active %s\n",
epicsThreadGetNameSelf(), precord->name);
+48 -11
View File
@@ -3,6 +3,7 @@
#include <map>
#include <asLib.h>
#include <dbAccess.h>
#include <dbChannel.h>
#include <dbStaticLib.h>
@@ -15,7 +16,7 @@
#include <pv/pvData.h>
#include <pv/anyscalar.h>
#include <shareLib.h>
#include <pv/qsrv.h>
#ifndef VERSION_INT
# define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P))
@@ -29,14 +30,20 @@
# define USE_MULTILOCK
#endif
namespace epics {
namespace pvAccess {
class ChannelRequester;
}
}
short PVD2DBR(epics::pvData::ScalarType pvt);
// copy from PVField (.value sub-field) to DBF buffer
epicsShareExtern
QSRV_API
long copyPVD2DBF(const epics::pvData::PVField::const_shared_pointer& in,
void *outbuf, short outdbf, long *outnReq);
// copy from DBF buffer to PVField (.value sub-field)
epicsShareExtern
QSRV_API
long copyDBF2PVD(const epics::pvData::shared_vector<const void>& buf,
const epics::pvData::PVField::shared_pointer& out,
epics::pvData::BitSet &changed,
@@ -62,7 +69,7 @@ union dbrbuf {
char dbf_STRING[MAX_STRING_SIZE];
};
struct epicsShareClass DBCH {
struct QSRV_API DBCH {
dbChannel *chan;
DBCH() :chan(0) {}
explicit DBCH(dbChannel *ch); // calls dbChannelOpen()
@@ -81,6 +88,23 @@ private:
void prepare();
};
struct ASCred {
// string storage must be safely mutable. cf. asAddClient()
std::vector<char> user, host;
std::vector<std::vector<char> > groups;
void update(const std::tr1::shared_ptr<epics::pvAccess::ChannelRequester>& request);
};
struct ASCLIENT {
ASCLIENTPVT aspvt;
std::vector<ASCLIENTPVT> grppvt;
ASCLIENT() :aspvt(0) {}
~ASCLIENT();
// ASCred storage must remain valid
void add(dbChannel* chan, ASCred& cred);
bool canWrite();
};
struct pdbRecordInfo {
DBENTRY ent;
pdbRecordInfo(const char *name)
@@ -254,7 +278,7 @@ struct DBManyLock
dbLocker *plock;
DBManyLock() :plock(NULL) {}
DBManyLock(const std::vector<dbCommon*>& recs, unsigned flags=0)
:plock(dbLockerAlloc((dbCommon**)&recs[0], recs.size(), flags))
:plock(dbLockerAlloc( (recs.size() > 0 ? (dbCommon**)&recs[0] : NULL), recs.size(), flags))
{
if(!plock) throw std::invalid_argument("Failed to create locker");
}
@@ -285,7 +309,7 @@ struct DBManyLocker
};
#endif
struct epicsShareClass FieldName
struct QSRV_API FieldName
{
struct Component {
std::string name;
@@ -328,7 +352,7 @@ private:
FieldName& operator=(const FieldName&);
};
struct epicsShareClass PVIF {
struct QSRV_API PVIF {
PVIF(dbChannel *ch);
virtual ~PVIF() {}
@@ -343,9 +367,9 @@ struct epicsShareClass PVIF {
//! Copy from PDB record to pvalue (call dbChannelGet())
//! caller must lock record
virtual void put(epics::pvData::BitSet& mask, unsigned dbe, db_field_log *pfl) =0;
//! Copy from pvalue to PDB record (call dbChannelPut())
//! May copy from pvalue to PDB record (call dbChannelPut())
//! caller must lock record
virtual epics::pvData::Status get(const epics::pvData::BitSet& mask, proc_t proc=ProcInhibit) =0;
virtual epics::pvData::Status get(const epics::pvData::BitSet& mask, proc_t proc=ProcInhibit, bool permit=true) =0;
//! Calculate DBE mask from changed bitset
virtual unsigned dbe(const epics::pvData::BitSet& mask) =0;
@@ -354,7 +378,19 @@ private:
PVIF& operator=(const PVIF&);
};
struct epicsShareClass PVIFBuilder {
/** Factory for PVIF instances.
*
* Caller first passes a mapping type (eg. "scalar") to PVIFBuilder::create()
* to obtain a PVIFBuilder which may then by used to create PVIF instances
* for specific dbChannel.
*
* Caller than uses PVIFBuilder::dtype() to obtain (sub)Field descriptions.
* eg. more than one of these may be composed into an overall Structure description.
*
* Caller than creates a PVStructure and uses PVIFBuilder::attach() to
* build mappings for each dbChannel in the composed locations.
*/
struct QSRV_API PVIFBuilder {
virtual ~PVIFBuilder() {}
@@ -370,6 +406,7 @@ struct epicsShareClass PVIFBuilder {
// must be the root structure
virtual PVIF* attach(dbChannel *channel, const epics::pvData::PVStructurePtr& root, const FieldName& fld) =0;
// entry point for Builder
static PVIFBuilder* create(const std::string& name);
protected:
PVIFBuilder() {}
@@ -378,7 +415,7 @@ private:
PVIFBuilder& operator=(const PVIFBuilder&);
};
struct epicsShareClass ScalarBuilder : public PVIFBuilder
struct QSRV_API ScalarBuilder : public PVIFBuilder
{
virtual ~ScalarBuilder() {}
-2
View File
@@ -18,8 +18,6 @@
#include <pv/serverContext.h>
#include <pv/iocshelper.h>
#define epicsExportSharedSymbols
#include "pv/qsrv.h"
#include "pvahelper.h"
#include "pvif.h"
+217 -207
View File
@@ -7,242 +7,252 @@
* found in the file LICENSE that is included with this distribution.
\*************************************************************************/
/* Copyed from EPICS Base 3.16 branch */
/* Author: Andrew Johnson Date: 2003-04-08 */
/* Author: Andrew Johnson Date: 2003-04-08 */
/* Usage:
* softIoc [-D softIoc.dbd] [-h] [-S] [-s] [-a ascf]
* [-m macro=value,macro2=value2] [-d file.db]
* [-x prefix] [st.cmd]
*
* If used the -D option must come first, and specify the
* path to the softIoc.dbd file. The compile-time install
* location is saved in the binary as a default.
*
* Usage information will be printed if -h is given, then
* the program will exit normally.
*
* The -S option prevents an interactive shell being started
* after all arguments have been processed.
*
* Previous versions accepted a -s option to cause a shell
* to be started; this option is still accepted but ignored
* since a command shell is now started by default.
*
* Access Security can be enabled with the -a option giving
* the name of the configuration file; if any macros were
* set with -m before the -a option was given, they will be
* used as access security substitution macros.
*
* Any number of -m and -d arguments can be interspersed;
* the macros are applied to the following .db files. Each
* later -m option causes earlier macros to be discarded.
*
* The -x option loads the softIocExit.db with the macro
* IOC set to the string provided. This database contains
* a subroutine record named $(IOC):exit which has its field
* SNAM set to "exit". When this record is processed, the
* subroutine that runs will call epicsExit() with the value
* of the field A determining whether the exit status is
* EXIT_SUCCESS if (A == 0.0) or EXIT_FAILURE (A != 0.0).
*
* A st.cmd file is optional. If any databases were loaded
* the st.cmd file will be run *after* iocInit. To perform
* iocsh commands before iocInit, all database loading must
* be performed by the script itself, or by the user from
* the interactive IOC shell.
*/
#include <stddef.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <iostream>
#include <string>
#include <list>
#include <stdexcept>
#include <epicsVersion.h>
#include <epicsGetopt.h>
#include "registryFunction.h"
#include "epicsThread.h"
#include "epicsExit.h"
#include "epicsStdio.h"
#include "epicsString.h"
#include "dbStaticLib.h"
#include "subRecord.h"
#include "dbAccess.h"
#include "asDbLib.h"
#include "iocInit.h"
#include "iocsh.h"
#include "osiFileName.h"
extern "C" int softIocPVA_registerRecordDeviceDriver(struct dbBase *pdbbase);
#ifdef __rtems__
#define DBD_FILE "dbd/softIocPVA.dbd"
#define EXIT_FILE "db/softIocExit.db"
#else
#define DBD_FILE FINAL_LOCATION "/dbd/softIocPVA.dbd"
#define EXIT_FILE FINAL_LOCATION "/db/softIocExit.db"
#ifndef EPICS_BASE
// so IDEs knows EPICS_BASE is a string constant
# define EPICS_BASE "/"
# error -DEPICS_BASE required
#endif
#ifdef VERSION_INT
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
#define USE_EXIT_LATER
#if EPICS_VERSION_INT>=VERSION_INT(7,0,2,0)
# define USE_EXECDIR
#endif
#endif
const char *arg0;
const char *base_dbd = DBD_FILE;
const char *exit_db = EXIT_FILE;
#define DBD_BASE "dbd" OSI_PATH_SEPARATOR "softIocPVA.dbd"
#define EXIT_BASE "db" OSI_PATH_SEPARATOR "softIocExit.db"
#define DBD_FILE_REL ".." OSI_PATH_SEPARATOR ".." OSI_PATH_SEPARATOR DBD_BASE
#define EXIT_FILE_REL ".." OSI_PATH_SEPARATOR ".." OSI_PATH_SEPARATOR EXIT_BASE
#define DBD_FILE EPICS_BASE OSI_PATH_SEPARATOR DBD_BASE
#define EXIT_FILE EPICS_BASE OSI_PATH_SEPARATOR EXIT_BASE
namespace {
static void exitSubroutine(subRecord *precord) {
#ifdef USE_EXIT_LATER
epicsExitLater((precord->a == 0.0) ? EXIT_SUCCESS : EXIT_FAILURE);
#else
epicsExit((precord->a == 0.0) ? EXIT_SUCCESS : EXIT_FAILURE);
#endif
}
static void usage(int status) {
printf("Usage: %s [-D softIoc.dbd] [-h] [-S] [-a ascf]\n", arg0);
puts("\t[-m macro=value,macro2=value2] [-d file.db]");
puts("\t[-x prefix] [st.cmd]");
puts("Compiled-in path to softIocPVA.dbd is:");
printf("\t%s\n", base_dbd);
epicsExit(status);
void usage(const char *arg0, const std::string& base_dbd) {
std::cout<<"Usage: "<<arg0<<
" [-D softIocPVA.dbd] [-h] [-S] [-s] [-a ascf]\n"
"[-m macro=value,macro2=value2] [-d file.db]\n"
"[-x prefix] [st.cmd]\n"
"\n"
" -D <dbd> If used, must come first. Specify the path to the softIocPVA.dbdfile."
" The compile-time install location is saved in the binary as a default.\n"
"\n"
" -h Print this mesage and exit.\n"
"\n"
" -S Prevents an interactive shell being started.\n"
"\n"
" -s Previously caused a shell to be started. Now accepted and ignored.\n"
"\n"
" -a <acf> Access Security configuration file. Macro substitution is\n"
" performed.\n"
"\n"
" -m <MAC>=<value>,... Set/replace macro definitions used by subsequent -d and\n"
" -a.\n"
"\n"
" -d <db> Load records from file (dbLoadRecords). Macro substitution is\n"
" performed.\n"
"\n"
" -x <prefix> Load softIocExit.db. Provides a record \"<prefix>:exit\".\n"
" Put 0 to exit with success, or non-zero to exit with an error.\n"
"\n"
"Any number of -m and -d arguments can be interspersed; the macros are applied\n"
"to the following .db files. Each later -m option causes earlier macros to be\n"
"discarded.\n"
"\n"
"A st.cmd file is optional. If any databases were loaded the st.cmd file will\n"
"be run *after* iocInit. To perform iocsh commands before iocInit, all database\n"
"loading must be performed by the script itself, or by the user from the\n"
"interactive IOC shell.\n"
"\n"
"Compiled-in path to softIocPVA.dbd is:\n"
"\t"<<base_dbd.c_str()<<"\n";
}
void errIf(int ret, const std::string& msg)
{
if(ret)
throw std::runtime_error(msg);
}
bool lazy_dbd_loaded;
void lazy_dbd(const std::string& dbd_file) {
if(lazy_dbd_loaded) return;
lazy_dbd_loaded = true;
errIf(dbLoadDatabase(dbd_file.c_str(), NULL, NULL),
std::string("Failed to load DBD file: ")+dbd_file);
std::cout<<"dbLoadDatabase(\""<<dbd_file<<"\")\n";
softIocPVA_registerRecordDeviceDriver(pdbbase);
std::cout<<"softIocPVA_registerRecordDeviceDriver(pdbbase)\n";
registryFunctionAdd("exit", (REGISTRYFUNCTION) exitSubroutine);
}
} // namespace
int main(int argc, char *argv[])
{
char *dbd_file = const_cast<char*>(base_dbd);
char *macros = NULL;
char xmacro[PVNAME_STRINGSZ + 4];
int startIocsh = 1; /* default = start shell */
int loadedDb = 0;
arg0 = strrchr(*argv, '/');
if (!arg0) {
arg0 = *argv;
} else {
++arg0; /* skip the '/' */
}
--argc, ++argv;
/* Do this here in case the dbd file not available */
if (argc>0 && **argv=='-' && (*argv)[1]=='h') {
usage(EXIT_SUCCESS);
}
if (argc>1 && **argv=='-' && (*argv)[1]=='D') {
dbd_file = *++argv;
argc -= 2;
++argv;
}
if (dbLoadDatabase(dbd_file, NULL, NULL)) {
epicsExit(EXIT_FAILURE);
}
softIocPVA_registerRecordDeviceDriver(pdbbase);
registryFunctionAdd("exit", (REGISTRYFUNCTION) exitSubroutine);
try {
std::string dbd_file(DBD_FILE),
exit_file(EXIT_FILE),
macros, // scratch space for macros (may be given more than once)
xmacro;
bool interactive = true;
bool loadedDb = false;
while (argc>1 && **argv == '-') {
switch ((*argv)[1]) {
case 'a':
if (macros) asSetSubstitutions(macros);
asSetFilename(*++argv);
--argc;
break;
case 'd':
if (dbLoadRecords(*++argv, macros)) {
epicsExit(EXIT_FAILURE);
}
loadedDb = 1;
--argc;
break;
case 'h':
usage(EXIT_SUCCESS);
case 'm':
macros = *++argv;
--argc;
break;
case 'S':
startIocsh = 0;
break;
case 's':
break;
case 'x':
epicsSnprintf(xmacro, sizeof xmacro, "IOC=%s", *++argv);
if (dbLoadRecords(exit_db, xmacro)) {
epicsExit(EXIT_FAILURE);
}
loadedDb = 1;
--argc;
break;
default:
printf("%s: option '%s' not recognized\n", arg0, *argv);
usage(EXIT_FAILURE);
}
--argc;
++argv;
#ifdef USE_EXECDIR
// attempt to compute relative paths
{
std::string prefix;
char *cprefix = epicsGetExecDir();
if(cprefix) {
try {
prefix = cprefix;
free(cprefix);
} catch(...) {
free(cprefix);
throw;
}
}
dbd_file = prefix + DBD_FILE_REL;
exit_file = prefix + EXIT_FILE_REL;
}
#endif
int opt;
while ((opt = getopt(argc, argv, "ha:D:d:m:Ssx:")) != -1) {
switch (opt) {
case 'h': /* Print usage */
usage(argv[0], dbd_file);
epicsExit(0);
return 0;
default:
usage(argv[0], dbd_file);
std::cerr<<"Unknown argument: -"<<char(opt)<<"\n";
epicsExit(2);
return 2;
case 'a':
lazy_dbd(dbd_file);
if (!macros.empty()) {
if(asSetSubstitutions(macros.c_str()))
throw std::bad_alloc();
std::cout<<"asSetSubstitutions(\""<<macros<<"\")\n";
}
if(asSetFilename(optarg))
throw std::bad_alloc();
std::cout<<"asSetFilename(\""<<optarg<<"\")\n";
break;
case 'D':
if(lazy_dbd_loaded) {
throw std::runtime_error("-D specified too late. softIocPVA.dbd already loaded.\n");
}
dbd_file = optarg;
break;
case 'd':
lazy_dbd(dbd_file);
errIf(dbLoadRecords(optarg, macros.c_str()),
std::string("Failed to load: ")+optarg);
std::cout<<"dbLoadRecords(\""<<optarg<<"\"";
if(!macros.empty())
std::cout<<", \""<<macros<<"\"";
std::cout<<")\n";
loadedDb = true;
break;
case 'm':
macros = optarg;
break;
case 'S':
interactive = false;
break;
case 's':
break; // historical
case 'x':
lazy_dbd(dbd_file);
xmacro = "IOC=";
xmacro += optarg;
errIf(dbLoadRecords(exit_file.c_str(), xmacro.c_str()),
std::string("Failed to load: ")+exit_file);
loadedDb = true;
break;
}
}
lazy_dbd(dbd_file);
if(optind<argc) {
// run script
// ignore any extra positional args (historical)
std::cout<<"# Begin "<<argv[optind]<<"\n";
errIf(iocsh(argv[optind]),
std::string("Error in ")+argv[optind]);
std::cout<<"# End "<<argv[optind]<<"\n";
epicsThreadSleep(0.2);
loadedDb = true; /* Give it the benefit of the doubt... */
}
if (loadedDb) {
std::cout<<"iocInit()\n";
iocInit();
epicsThreadSleep(0.2);
}
if(interactive) {
std::cout.flush();
std::cerr.flush();
if(iocsh(NULL)) {
epicsExit(1);
return 1;
}
} else {
if (loadedDb) {
epicsThreadExitMain();
} else {
usage(argv[0], dbd_file);
std::cerr<<"Nothing to do!\n";
epicsExit(1);
return 1;
}
}
epicsExit(0);
return 0;
}catch(std::exception& e){
std::cerr<<"Error: "<<e.what()<<"\n";
epicsExit(2);
return 2;
}
if (argc>0 && **argv=='-') {
switch((*argv)[1]) {
case 'a':
case 'd':
case 'm':
case 'x':
printf("%s: missing argument to option '%s'\n", arg0, *argv);
usage(EXIT_FAILURE);
case 'h':
usage(EXIT_SUCCESS);
case 'S':
startIocsh = 0;
break;
case 's':
break;
default:
printf("%s: option '%s' not recognized\n", arg0, *argv);
usage(EXIT_FAILURE);
}
--argc;
++argv;
}
if (loadedDb) {
iocInit();
epicsThreadSleep(0.2);
}
/* run user's startup script */
if (argc>0) {
if (iocsh(*argv)) epicsExit(EXIT_FAILURE);
epicsThreadSleep(0.2);
loadedDb = 1; /* Give it the benefit of the doubt... */
}
/* start an interactive shell if it was requested */
if (startIocsh) {
iocsh(NULL);
} else {
if (loadedDb) {
epicsThreadExitMain();
} else {
printf("%s: Nothing to do!\n", arg0);
usage(EXIT_FAILURE);
}
}
epicsExit(EXIT_SUCCESS);
/*Note that the following statement will never be executed*/
return 0;
}
-1
View File
@@ -9,7 +9,6 @@
#include <pv/sharedPtr.h>
#define epicsExportSharedSymbols
#include "helper.h"
#include "tpool.h"
-2
View File
@@ -13,8 +13,6 @@
#include <pv/sharedPtr.h>
#include <shareLib.h>
struct WorkQueue : private epicsThreadRunable
{
typedef std::tr1::weak_ptr<epicsThreadRunable> value_type;
+1 -1
View File
@@ -44,7 +44,7 @@ testpdb_SRCS += p2pTestIoc_registerRecordDeviceDriver.cpp
testpdb_LIBS += qsrv
TESTS += testpdb
PROD_HOST += check_consist
TESTPROD_HOST += check_consist
check_consist_SRCS += check_consist.cpp
ifdef BASE_3_16
+23 -23
View File
@@ -1,8 +1,8 @@
#include <dbUnitTest.h>
#include <testMain.h>
#include <longinRecord.h>
#include <longoutRecord.h>
#include <int64inRecord.h>
#include <int64outRecord.h>
#include <pv/qsrv.h>
#include "utilities.h"
@@ -15,50 +15,50 @@ void testGet()
{
testDiag("==== testGet ====");
longinRecord *li1 = (longinRecord*)testdbRecordPtr("src:i1");
int64inRecord *i1 = (int64inRecord*)testdbRecordPtr("src:i1");
while(!dbIsLinkConnected(&li1->inp))
testqsrvWaitForLinkEvent(&li1->inp);
while(!dbIsLinkConnected(&i1->inp))
testqsrvWaitForLinkEvent(&i1->inp);
testdbGetFieldEqual("target:i.VAL", DBF_INT64, 42);
testdbGetFieldEqual("target:i.VAL", DBF_INT64, 42LL);
testdbGetFieldEqual("src:i1.VAL", DBF_INT64, 0); // value before first process
testdbGetFieldEqual("src:i1.VAL", DBF_INT64, 0LL); // value before first process
testdbGetFieldEqual("src:i1.INP", DBF_STRING, "{\"pva\":\"target:i\"}");
testdbPutFieldOk("src:i1.PROC", DBF_INT64, 1);
testdbPutFieldOk("src:i1.PROC", DBF_INT64, 1LL);
testdbGetFieldEqual("src:i1.VAL", DBF_INT64, 42);
testdbGetFieldEqual("src:i1.VAL", DBF_INT64, 42LL);
testdbPutFieldOk("src:i1.INP", DBF_STRING, "{\"pva\":\"target:ai\"}");
while(!dbIsLinkConnected(&li1->inp))
testqsrvWaitForLinkEvent(&li1->inp);
while(!dbIsLinkConnected(&i1->inp))
testqsrvWaitForLinkEvent(&i1->inp);
testdbGetFieldEqual("src:i1.VAL", DBF_INT64, 42); // changing link doesn't automatically process
testdbGetFieldEqual("src:i1.VAL", DBF_INT64, 42LL); // changing link doesn't automatically process
testdbPutFieldOk("src:i1.PROC", DBF_INT64, 1);
testdbPutFieldOk("src:i1.PROC", DBF_INT64, 1LL);
testdbGetFieldEqual("src:i1.VAL", DBF_INT64, 4); // now it's changed
testdbGetFieldEqual("src:i1.VAL", DBF_INT64, 4LL); // now it's changed
}
void testPut()
{
testDiag("==== testPut ====");
longoutRecord *lo2 = (longoutRecord*)testdbRecordPtr("src:lo2");
int64outRecord *o2 = (int64outRecord*)testdbRecordPtr("src:o2");
while(!dbIsLinkConnected(&lo2->out))
testqsrvWaitForLinkEvent(&lo2->out);
while(!dbIsLinkConnected(&o2->out))
testqsrvWaitForLinkEvent(&o2->out);
testdbGetFieldEqual("target:i2.VAL", DBF_INT64, 43);
testdbGetFieldEqual("src:lo2.VAL", DBF_INT64, 0);
testdbGetFieldEqual("src:lo2.OUT", DBF_STRING, "{\"pva\":\"target:i2\"}");
testdbGetFieldEqual("target:i2.VAL", DBF_INT64, 43LL);
testdbGetFieldEqual("src:o2.VAL", DBF_INT64, 0LL);
testdbGetFieldEqual("src:o2.OUT", DBF_STRING, "{\"pva\":\"target:i2\"}");
testdbPutFieldOk("src:lo2.VAL", DBF_INT64, 14);
testdbPutFieldOk("src:o2.VAL", DBF_INT64, 14LL);
testdbGetFieldEqual("target:i2.VAL", DBF_INT64, 14);
testdbGetFieldEqual("src:lo2.VAL", DBF_INT64, 14);
testdbGetFieldEqual("target:i2.VAL", DBF_INT64, 14LL);
testdbGetFieldEqual("src:o2.VAL", DBF_INT64, 14LL);
}
} // namespace
+1 -1
View File
@@ -16,6 +16,6 @@ record(int64in, "target:i2") {
field(VAL, "43")
}
record(int64out, "src:lo2") {
record(int64out, "src:o2") {
field(OUT, {pva:"target:i2"})
}