Merge lp:~epics-core/epics-base/rsrvbindiface into lp:~epics-core/epics-base/3.16

Proposed by mdavidsaver
Status: Superseded
Proposed branch: lp:~epics-core/epics-base/rsrvbindiface
Merge into: lp:~epics-core/epics-base/3.16
Diff against target: 3880 lines (+2343/-583) (has conflicts)
38 files modified
configure/CONFIG_BASE_VERSION (+4/-0)
configure/RULES_EXPAND (+39/-0)
documentation/KnownProblems.html (+19/-0)
documentation/RELEASE_NOTES.html (+1162/-0)
src/ca/client/addrList.h (+1/-1)
src/ca/client/iocinf.cpp (+6/-5)
src/ca/client/udpiiu.cpp (+13/-0)
src/ioc/dbStatic/dbLex.l (+8/-3)
src/ioc/dbStatic/dbLexRoutines.c (+1/-0)
src/ioc/dbStatic/dbStaticLib.c (+4/-1)
src/ioc/rsrv/camessage.c (+8/-31)
src/ioc/rsrv/caserverio.c (+1/-1)
src/ioc/rsrv/caservertask.c (+557/-164)
src/ioc/rsrv/cast_server.c (+59/-95)
src/ioc/rsrv/online_notify.c (+14/-196)
src/ioc/rsrv/server.h (+20/-9)
src/libCom/env/envDefs.h (+2/-0)
src/libCom/env/envSubr.c (+14/-1)
src/libCom/osi/Makefile (+4/-0)
src/libCom/osi/os/Darwin/osdSock.h (+1/-0)
src/libCom/osi/os/Linux/osdSock.h (+1/-0)
src/libCom/osi/os/RTEMS/osdSock.h (+1/-0)
src/libCom/osi/os/WIN32/osdSock.h (+1/-0)
src/libCom/osi/os/cygwin32/osdSock.h (+1/-0)
src/libCom/osi/os/default/epicsMMIODef.h (+7/-6)
src/libCom/osi/os/default/osdNetIntf.c (+19/-25)
src/libCom/osi/os/freebsd/osdSock.h (+1/-0)
src/libCom/osi/os/iOS/osdSock.h (+1/-0)
src/libCom/osi/os/solaris/osdSock.h (+1/-0)
src/libCom/osi/os/vxWorks/epicsMMIO.h (+6/-1)
src/libCom/osi/os/vxWorks/osdSock.h (+1/-0)
src/libCom/osi/os/vxWorks/osdTime.cpp (+37/-5)
src/libCom/osi/osiClockTime.c (+59/-32)
src/std/filters/filters.dbd.pod (+7/-7)
src/tools/Makefile (+1/-0)
src/tools/assembleSnippets.pl (+151/-0)
src/tools/test/Makefile (+1/-0)
src/tools/test/Snippets.plt (+110/-0)
Text conflict in configure/CONFIG_BASE_VERSION
Text conflict in documentation/KnownProblems.html
Text conflict in documentation/RELEASE_NOTES.html
To merge this branch: bzr merge lp:~epics-core/epics-base/rsrvbindiface
Reviewer Review Type Date Requested Status
mdavidsaver Needs Information
Ralph Lange Pending
Andrew Johnson Pending
EPICS Core Developers Pending
Review via email: mp+281118@code.launchpad.net

This proposal has been superseded by a proposal from 2016-01-12.

Description of the change

Updates to RSRV, mainly to allow binding to a specific network interfaces via. EPICS_CAS_INTF_ADDR_LIST. This necessitates quite a lot of work to "un-globalize" RSRV.

Along the way support for name lookup via IPv4 multicasting is added, but not enabled by default. Use by adding an mcast address to EPICS_CA_ADDR_LIST.

One potentially troublesome change is to allow osiSockDiscoverBroadcastAddresses() to find the loopback broadcast address.

To post a comment you must log in.
Revision history for this message
mdavidsaver (mdavidsaver) wrote :

Needs compatibility testing

review: Needs Information
12715. By Bjorklund, Eric A <email address hidden>

vxWorks: missing extern "C" around sysPci* prototypes

12716. By mdavidsaver

epicsMMIODef.h use static inline

'static inline' is necessary for consistent behavior with C and C++.
Plain 'inline' in C can result in multiply defined symbol
errors when linking.

12717. By mdavidsaver

rsrv: when binding to a specific interface also bind to iface bcast

An oddness of BSD sockets (not winsock) is that binding to
INADDR_ANY will receive unicast and broadcast, but binding to
a specific interface address receives only unicast. The trick
is to bind a second socket to the interface broadcast address,
which will then receive only broadcasts.

12718. By mdavidsaver

rsrv: unnecessary use of global

12719. By mdavidsaver

remove global prsrv_cast_client

12720. By mdavidsaver

rename prsrv_cast_client in cast_server()

12721. By mdavidsaver

rsrv: placeholder for cast_server thread shutdown

12722. By mdavidsaver

rsrv: more renaming

12723. By mdavidsaver

libCom/osi: osiSockDiscoverBroadcastAddresses() finds 127.255.255.255

For the default implementation of osiSockDiscoverBroadcastAddresses()
allow the loopback interface to have IFF_BROADCAST set (usually isn't)
and on Linux assume that the loopback broadcast address is usable
even if IFF_BROADCAST isn't set.

12724. By mdavidsaver

rsrv: casr show multiple UDP clients

12725. By mdavidsaver

rsrv: UDP listener on more than one iface

12726. By mdavidsaver

rsrv: unglobalize IOC_sock

12727. By mdavidsaver

rsrv: no lazy init of BUCKET

12728. By mdavidsaver

rsrv: move some from req_server to rsrv_init

12729. By mdavidsaver

rsrv: redo initialization to support bind multiple interfaces.

consolidate most setup tasks in rsrv_init(),
which now spawns threads directly.
For each interface create 3-4 sockets,

* TCP listener
* UDP receiver (unicast)
* UDP receiver (broadcast optional)
* UDP beacon sender

12730. By mdavidsaver

envDefs: add envGetBoolConfigParam

12731. By mdavidsaver

libca: addAddrToChannelAccessAddressList report success

12732. By mdavidsaver

expand beacon address list when binding to wildcard

12733. By mdavidsaver

rsrv: additional fallback for interface list

12734. By mdavidsaver

rsrv: don't leak sockets when building tcp set

12735. By mdavidsaver

rsrv: multicast name lookup

12736. By mdavidsaver

libca: enable mcast loopback

12737. By Andrew Johnson

Fix build for vxWorks 5.5.2

12738. By mdavidsaver

minor

fix typo
expand error message
skip redundant (and misleading) print of beacon address list

12739. By mdavidsaver

rsrv: fixup beacon address list handling

should be based on client interface list,
not server.

12740. By mdavidsaver

rsrv: loopback mcast beacons

12741. By mdavidsaver

don't use EPICS_CA_ADDR_LIST for server interface list

keep existing behavior, and server binds wildcard
when only client address list is specified.

12742. By mdavidsaver

update release notes

12743. By mdavidsaver

pcas: listen for mcast search request

12744. By Andrew Johnson

Fix build for vxWorks 5.5.2 (again)

12745. By mdavidsaver

rsrv: beaconCounter not updated

12746. By mdavidsaver

rsrv: fix wildcard check ordering

filter out mcast addresses before wildcard uniqueness check.

12747. By mdavidsaver

rsrv: oops

improper rebase

12748. By mdavidsaver

rsrv: more beacon address fixups

should really be based on *server* config
as beacons should be sent to clients attempting
to connect to the server.

By default send beacons to bcast addresses
from the interface address list.

12749. By Andrew Johnson

Improve casr() output

12750. By mdavidsaver

rsrv: decouple some client/server config

the automatic beacon address list should follow
the server interface address list, and not be
effected by any client config.

Also, don't populate the interface address list
from EPICS_CAS_BEACON_ADDR_LIST as this will
almost never contain local interface addresses.

12751. By mdavidsaver

pcas: no fallbacks for beacon address list

Don't mix client and server configuration
by defaulting the server beacon list to use
the client address list.

12752. By mdavidsaver

ca: update CAref.html with changes to beacon address list

Note that server beacon address list will no longer
consider client address list as a fallback.

12753. By mdavidsaver

update release notes

12754. By mdavidsaver

rsrv: EPICS_CAS_IGNORE_ADDR_LIST

12755. By mdavidsaver

libCom: fix debug print formats in osiSockDiscoverBroadcastAddresses()

12756. By mdavidsaver

rsrv: "casr 1" shows ignore list

12757. By mdavidsaver

update release notes

12758. By Andrew Johnson

Show UDP name server status in casr 2+ output

12759. By mdavidsaver

pcas: don't send beacons w/ connect()d socket

use sendto() and leave the origin field to 0.0.0.0
so it will be filled in by the receiving repeater

12760. By Ralph Lange

pcas: fix compilation on MSC Windows

12761. By Andrew Johnson

Report UDP client status on Windows

12762. By Ralph Lange

rsrv: populate the server list - also on Windows builds

12763. By mdavidsaver

win32: include ws2tcpip.h from osdSock.h

Needed to get some additional definitions
including IP_ADD_MEMBERSHIP

12764. By mdavidsaver

cygwin inherits winsock broadcast behavior

12765. By mdavidsaver

rsrv: use SOCKERRNO

Must use OSI SOCKERRNO for correct handling
of errors w/ winsock

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'configure/CONFIG_BASE_VERSION'
2--- configure/CONFIG_BASE_VERSION 2015-06-05 18:48:57 +0000
3+++ configure/CONFIG_BASE_VERSION 2016-01-12 02:07:47 +0000
4@@ -40,7 +40,11 @@
5 EPICS_REVISION = 16
6
7 # EPICS_MODIFICATION must be a number >=0 and <256
8+<<<<<<< TREE
9 EPICS_MODIFICATION = 0
10+=======
11+EPICS_MODIFICATION = 3
12+>>>>>>> MERGE-SOURCE
13
14 # EPICS_PATCH_LEVEL must be a number (win32 resource file requirement)
15 # Not included if zero
16
17=== modified file 'configure/RULES_EXPAND'
18--- configure/RULES_EXPAND 2014-09-25 20:49:44 +0000
19+++ configure/RULES_EXPAND 2016-01-12 02:07:47 +0000
20@@ -2,6 +2,9 @@
21
22 vpath %@ $(USR_VPATH) $(ALL_SRC_DIRS)
23
24+#---------------------------------------------------------------
25+# Variable expansion
26+
27 # Default settings
28 EXPAND_TOOL ?= $(PERL) $(TOOLS)/expandVars.pl
29
30@@ -24,3 +27,39 @@
31 @$(RM) $(EXPANDED)
32
33 .PHONY : expand_clean
34+
35+#---------------------------------------------------------------
36+# Assemblies (files assembled from snippets)
37+
38+ASSEMBLE_TOOL ?= $(PERL) $(TOOLS)/assembleSnippets.pl
39+
40+define COMMON_ASSEMBLY_template
41+$1_SNIPPETS += $$(foreach dir, .. $$(SRC_DIRS), \
42+ $$(wildcard $$(dir)/$$($1_PATTERN)))
43+$(COMMON_DIR)/$1: $$($1_SNIPPETS)
44+ $(ECHO) "Assembling common file $$@ from snippets"
45+ @$(RM) $1
46+ $(ASSEMBLE_TOOL) -o $1 $$^
47+ @$(MV) $1 $$@
48+endef
49+$(foreach asy, $(COMMON_ASSEMBLIES), \
50+ $(eval $(call COMMON_ASSEMBLY_template,$(strip $(asy)))))
51+
52+define ASSEMBLY_template
53+$1_SNIPPETS += $$(foreach dir, .. $$(SRC_DIRS), \
54+ $$(wildcard $$(dir)/$$($1_PATTERN)))
55+$1: $$($1_SNIPPETS)
56+ $(ECHO) "Assembling file $$@ from snippets"
57+ @$(RM) $$@
58+ $(ASSEMBLE_TOOL) -o $$@ $$^
59+endef
60+$(foreach asy, $(ASSEMBLIES), \
61+ $(eval $(call ASSEMBLY_template,$(strip $(asy)))))
62+
63+define ASSEMBLY_DEP_template
64+$1$(DEP):
65+ @echo $1: > $$@
66+endef
67+$(foreach asy, $(sort $(COMMON_ASSEMBLIES) $(ASSEMBLIES)), \
68+ $(eval $(call ASSEMBLY_DEP_template,$(strip $(asy)))))
69+
70
71=== modified file 'documentation/KnownProblems.html'
72--- documentation/KnownProblems.html 2015-06-05 18:48:57 +0000
73+++ documentation/KnownProblems.html 2016-01-12 02:07:47 +0000
74@@ -22,10 +22,29 @@
75
76 <ul>
77
78+<<<<<<< TREE
79 <!--
80 <li>YYYY-MM-DD: Description of problem
81 ...</li>
82 -->
83+=======
84+ <li>See LaunchPad bug
85+ <a href="https://bugs.launchpad.net/epics-base/+bug/1466129">#1466129</a>:
86+ The feature that allows an IOC to bind to a single network interface is not
87+ working correctly. The bug mentioned above contains a patch that has been
88+ tested on Linux-64, a complete fix is expected for 3.15.4. Note that Windows
89+ systems are not affected by this bug.</li>
90+
91+ <li>Parallel builds ("make -j") on native Windows are not working properly.
92+ Builds tend to hang (saturating one core); interrupting and running make
93+ again usually finishes the build. Limiting the number of parallel jobs using
94+ something like "make -j8" also helps prevent this problem. Sequential builds
95+ always work and are recommended for automated build environments.</li>
96+
97+ <li>64-bit Windows builds of the CAS library may not work with some compilers.
98+ The code in <tt>src/legacy/gdd</tt> is currently incompatible with the LLP64
99+ model that Windows uses for its 64-bit ABI.</li>
100+>>>>>>> MERGE-SOURCE
101
102 </ul>
103
104
105=== modified file 'documentation/RELEASE_NOTES.html'
106--- documentation/RELEASE_NOTES.html 2015-09-07 19:23:04 +0000
107+++ documentation/RELEASE_NOTES.html 2016-01-12 02:07:47 +0000
108@@ -11,6 +11,7 @@
109
110 <p style="color:red">This version of EPICS Base has not been released yet.</p>
111
112+<<<<<<< TREE
113 <h2 align="center">Changes between 3.15.x and 3.16.0</h2>
114 <!-- Insert new items immediately below this template ...
115
116@@ -85,6 +86,1167 @@
117 <p>Added a new macro <tt>callbackGetPriority(prio, callback)</tt> to the
118 callback.h header and removed the need for dbScan.c to reach into the internals
119 of its CALLBACK objects.</p>
120+=======
121+<h2 align="center">Changes between 3.15.2 and 3.15.3</h2>
122+<!-- Insert new items immediately below here ... -->
123+
124+<h3>Make the NTP Time provider optional on VxWorks</h3>
125+
126+<p>Recent versions of VxWorks (sometime after VxWorks 6) provide facilities for
127+automatically synchronizing the OS clock time with an NTP server. The EPICS time
128+system used to assume that it had to provide time synchronization on VxWorks,
129+but now it tests for the existance of either of the two OS synchronization
130+threads before starting the NTP time provider. It is still possible to force the
131+NTP provider to be started even if the OS synchronization is running by defining
132+the environment variable <tt>EPICS_TS_FORCE_NTPTIME</tt> in the startup script
133+before loading the IOC's .munch file. Forcing may be necessary if the VxWorks
134+image is not correctly configured with the IP address of a local NTP server.</p>
135+
136+<h3>Assembling files from numbered snippets</h3>
137+
138+<p>A tool has been added that assembles file snippets specified on the
139+command line into a single output file, with sorting and replacing/adding of
140+snippets done based on their file names. The build system integration requires
141+the output file to be specified setting COMMON_ASSEMBLIES (arch independent)
142+or ASSEMBLIES (created by arch), then defining the snippets for each assembly
143+setting *_SNIPPETS (explicitly) or *_PATTERN (searched relative to all source
144+directories).
145+</p>
146+
147+<h3>Clean up after GNU readline()</h3>
148+
149+<p>If EPICS Base is built with readline support, any IOC that calls epicsExit()
150+from a thread other than the main thread is likely to leave the user's terminal
151+in a weird state, requiring the user to run something like 'stty sane' to clean
152+it up. This release patches the readline support code to clean up automatically
153+by registering an epicsAtExit() routine.</p>
154+
155+<h3>Removed the last vestiges of RSET::get_value()</h3>
156+
157+<p>The IOC has not called the get_value() routine in the RSET for a very long
158+time, but there was still one implementation left in the event record support
159+code, and a structure definition for one of the original arguments to that
160+routine was defined in recGbl.h. Both of these have now been removed.</p>
161+
162+<h2 align="center">Changes between 3.15.1 and 3.15.2</h2>
163+
164+<h3>Raised limit on link field length in database files</h3>
165+
166+<p>The length of INP/OUT link fields in database files was limited to 79 chars
167+by an internal buffer size in the db file parser. This limitation will go away
168+completely in 3.16, and has been statically raised to 255 chars for the 3.15
169+series.</p>
170+
171+<h3>aoRecord raw conversion overflows</h3>
172+
173+<p>The ao record type now checks converted raw values and limits them to the
174+32-bit integer range before writing them to the RVAL field. Previously value
175+overflows relied on Undefined Behaviour which could give different results on
176+different platforms. The ROFF fields of the ao and ai record types are now
177+DBF_ULONG to allow an ROFF setting of 0x80000000 to work properly.</p>
178+
179+<h3>Changes to &lt;top&gt;/cfg/* files</h3>
180+
181+<p>The order in which cfg/CONFIG* and cfg/RULES* files are included from support
182+applications listed in the configure/RELEASE* files has been changed. Previously
183+these files were included in the order in which the top areas are listed in the
184+RELEASE file, but it makes more sense to load them in reverse order since later
185+entries override earlier ones in Makefiles but the release file order is
186+supposed to allow earlier entries to take precedence over later ones. The same
187+change has been made to the inclusion of the &lt;top&gt;/configure/RULES_BUILD
188+files.</p>
189+
190+<p>Two new file types can also be provided in a module's cfg directory. Files
191+named TOP_RULES* will be included by the top-level Makefile of other modules
192+that refer to this module; files name DIR_RULES* will be included by all
193+Makefiles that merely descend into lower-level directories. The cfg/RULES* files
194+are only included when make is building code inside the O.&lt;arch&gt;
195+directories.</p>
196+
197+<p>The new cfg/DIR_RULES* file inclusion was designed to permit new recursive
198+make actions to be implemented by appending the name of the new action to the
199+ACTIONS variable. There must be a matching rule in one of the cfg/RULES* files
200+when doing this. Similar rules may also be defined in the cfg/TOP_RULES* and/or
201+cfg/DIR_RULES* files, but these should only state prerequisites and not directly
202+provide commands to be executed.</p>
203+
204+<h3>Build rules for RTEMS GESYS modules</h3>
205+
206+<p>RTEMS target builds can now be configured to make GESYS modules by changing
207+the <CODE>USE_GESYS=NO</code> setting in the file
208+configure/os/CONFIG_SITE.Common.RTEMS to <code>YES</code>.</p>
209+
210+<h3>Added Make variables for command-line use</h3>
211+
212+<p>The following variables are now used during the build process, reserved for
213+setting on the command-line only (Makefiles should continue to use the
214+<code>USR_</code> equivalents):</p>
215+
216+<ul>
217+<li>CMD_INCLUDES</li>
218+<li>CMD_CPPFLAGS</li>
219+<li>CMD_CFLAGS</li>
220+<li>CMD_CXXFLAGS</li>
221+<li>CMD_LDFLAGS</li>
222+<li>CMD_DBFLAGS</li>
223+<li>CMD_DBDFLAGS</li>
224+<li>CMD_REGRDDFLAGS</li>
225+<li>CMD_ARFLAGS</li>
226+</ul>
227+
228+<p>For example:</p>
229+
230+<blockquote><pre>
231+make CMD_INCLUDES=/opt/local/include CMD_LDFLAGS=-L/opt/local/lib
232+</pre></blockquote>
233+
234+<h3>Enhanced API for asTrapWrite listeners</h3>
235+
236+<p>External software such as the CA Put Logging module that registers a listener
237+with the asTrapWrite subsystem was not previously given access to the actual
238+data being sent by the CA client. In most cases this was not a problem as the
239+listener can look at the field being modified both before and after the
240+operation, but if the put processes the record which immediately overwrites the
241+new value, the client's value cannot be observed.</p>
242+
243+<p>This release adds three fields to the asTrapWriteMessage structure that is
244+passed to the listener routines. These new fields provide the CA data type, the
245+number of array elements, and a pointer to the source data buffer. This change
246+is completely backwards compatible with listener code written against the
247+original API. The new API can be detected at compile-time as follows:</p>
248+
249+<blockquote><pre>
250+#include "asLib.h"
251+
252+/* ... */
253+
254+#ifdef asTrapWriteWithData
255+ /* Enhanced API */
256+#endif
257+</pre></blockquote>
258+
259+<h3>Use of PATH_FILTER in Makefiles deprecated</h3>
260+
261+<p>The PATH_FILTER variable was being called to convert forward shashes
262+<tt><b>/</b></tt> in file paths into pairs of backward slashes
263+<tt><b>\\</b></tt> on Windows architectures. This has never been strictly
264+necessary, and was added about 10 years ago to get around some short-comings in
265+Windows tools at the time. All uses of PATH_FILTER in Base have now been
266+removed; the definition is still present, but will result in a warning being
267+printed if it is ever used.</p>
268+
269+<h3>Using msi for dependencies</h3>
270+
271+<p>To reduce confusion the msi program has been modified to allow the generation
272+of dependency rules by adding support for a <tt>-D</tt> option, and changing the
273+commands in RULES.Db to use this option instead of the mkmf.pl script. The new
274+build rules will not work with old versions of the msi program, so the command
275+variable name used in the rules has been changed from MSI to MSI3_15. Sites that
276+use a modified version of msi must provide support for both the <tt>-D</tt> and
277+<tt>-o&nbsp;outfile</tt> options, and should then point the MSI3_15 variable in
278+their applications' CONFIG_SITE files to that updated executable.</p>
279+
280+<h2 align="center">Changes between 3.15.0.2 and 3.15.1</h2>
281+
282+<h3>epicsStrnEscapedFromRaw() and epicsStrnRawFromEscaped()</h3>
283+
284+<p>These routines have been rewritten; the previous implementations did not
285+always behave exactly as specified.</p>
286+
287+<h3>Shared Library Versions</h3>
288+
289+<p>On architectures that can support it, the shared library version number for
290+libraries provided with Base has had the third component of the EPICS version
291+number added to it, thus libCom.so.3.15.1 instead of libCom.so.3.15. Windows
292+can only support two components to its internal product version number, and the
293+Darwin bug that external shared libraries were being built using the EPICS
294+version number has been fixed.</p>
295+
296+<h3>Hooking into dbLoadRecords</h3>
297+
298+<p>A function pointer hook has been added to the dbLoadRecords() routine, to
299+allow external modules such as autosave to be notified when new records have
300+been loaded during IOC initialization. The hook is called dbLoadRecordsHook and
301+follows the model of the recGblAlarmHook pointer in that modules that wish to
302+use it must save the current value of the pointer before installing their own
303+function pointer, and must call the original function from their own
304+routine.</p>
305+
306+<p>The hook is activiated from the dbLoadRecords() routine and gets called only
307+after a database instance file has been read in without error. Note that the
308+dbLoadTemplates() routine directly calls dbLoadRecords() so this hook also
309+provides information about instantiated database templates. It is still possible
310+to load record instances using dbLoadDatabase() though, and doing this will not
311+result in the hook routines being called.</p>
312+
313+<p>Code to use this hook should look something like this:</p>
314+
315+<blockquote><pre>
316+#include "dbAccessDefs.h"
317+
318+static DB_LOAD_RECORDS_HOOK_ROUTINE previousHook;
319+
320+static void myRoutine(const char* file, const char* subs) {
321+ if (previousHook)
322+ previousHook(file, subs);
323+
324+ /* Do whatever ... */
325+}
326+
327+void myInit(void) {
328+ static int done = 0;
329+
330+ if (!done) {
331+ previousHook = dbLoadRecordsHook;
332+ dbLoadRecordsHook = myRoutine;
333+ done = 1;
334+ }
335+}
336+</pre></blockquote>
337+
338+<p>As with many other parts of the static database access library there is no
339+mutex to protect the function pointer. Initialization is expected to take place
340+in the context of the IOC's main thread, from either a static C++ constructor or
341+an EPICS registrar routine.</p>
342+
343+
344+<h2 align="center">Changes between 3.15.0.1 and 3.15.0.2</h2>
345+
346+<h3>New iocshLoad command</h3>
347+
348+<p>A new command <tt>iocshLoad</tt> has been added to iocsh which executes a
349+named iocsh script and can also set one or more shell macro variables at the
350+same time, the values of which will be forgotten immediately after the named
351+script finishes executing. The following example shows the syntax:</p>
352+
353+<blockquote><pre>
354+iocshLoad "serial.cmd", "DEV=/dev/ttyS0,PORT=com1,TYPE=RS485"
355+iocshLoad "radmon.cmd", "PORT=com1,ADDR=0"
356+</pre></blockquote>
357+
358+<h3>Support routines for 64-bit integers</h3>
359+
360+<p>The libCom library now provides support for 64-bit integer types on all
361+supported architectures. The epicsTypes.h header file defines epicsInt64 and
362+epicsUInt64 type definitions for both C and C++ code. The epicsStdlib.h header
363+also declares the following for parsing strings into the relevent sized integer
364+variables: Functions epicsParseLLong(), epicsParseULLong() with related macros
365+epicsScanLLong() and epicsScanULLong(), and the functions epicsParseInt64()
366+and epicsParseUInt64(). Use the first two functions and the macros for long long
367+and unsigned long long integer types, and the last two functions for the
368+epicsInt64 and epicsUInt64 types. Note that the latter can map to the types long
369+and unsigned long on some 64-bit architectures such as linux-x86_64, not to the
370+two long long types.</p>
371+
372+<p>This version does not provide the ability to define 64-bit record fields, the
373+use of the 64-bit types in the IOC database will come in a later release of
374+EPICS Base.</p>
375+
376+<h3>Full support for loadable support modules</h3>
377+
378+<p>Apparently later versions of Base 3.14 permitted support modules to be loaded
379+from a shared library at runtime without the IOC having been linked against that
380+shared library; the registerRecordDeviceDriver.pl program would accept a partial
381+DBD file containing just the entries needed for the library and generate the
382+appropriate registration code. In 3.15 however the registerRecordDeviceDriver.pl
383+program was replaced by one using the new DBD file parser, and in this a device
384+support entry would only be accepted after first loading the record type that it
385+depended on.</p>
386+
387+<p>The parser has been modified to accept device entries without having seen the
388+record type first, although a warning is given when that happens. To remove the
389+warning the DBD file can provide a record type declaration instead (no fields
390+can be defined, so the braces must be empty), before the device() entry. The
391+result will generate the correct registration code for the device entry without
392+including anything for any merely declared record types. The generated code can
393+be linked into a shared library and loaded by an IOC at runtime using dlload.
394+</p>
395+
396+<h3>Parallel callback threads</h3>
397+
398+<p>The general purpose callback facility can run multiple parallel callback
399+threads per priority level. This makes better use of SMP architectures (e.g.
400+processors with multiple cores), as callback work - which includes second
401+stage processing of records with asynchronuous device support and I/O
402+scanned processing - can be distributed over the available CPUs.</p>
403+
404+<p>Note that by using parallel callback threads the order of scan callback
405+requests in the queue is not retained. If a device support needs to be
406+informed when scanIoRequest processing has finished, it should use the new
407+scanIoSetComplete() feature to add a user function that will be called after
408+the scanIoRequest record processing has finished.</p>
409+
410+<p>Parallel callback threads have to be explicitly configured, by default
411+the IOC keeps the old behavior of running one callback thread per priority.</p>
412+
413+<h3>Merge MMIO API from devLib2</h3>
414+
415+<p>Added calls to handle 8, 16, and 32 bit Memory Mapped I/O reads and writes.
416+The calls added include <tt><i>X</i>_iowrite<i>Y</i>()</tt> and
417+<tt><i>X</i>_ioread<i>Y</i>()</tt>
418+where <tt><i>X</i></tt> is <tt>nat</tt> (native), <tt>be</tt> or <tt>le</tt>,
419+and <tt><i>Y</i></tt> is <tt>16</tt> or <tt>32</tt>.
420+Also added are <tt>ioread8()</tt> and <tt>iowrite8()</tt>.</p>
421+
422+<h3>Added optional dbServer API to database</h3>
423+
424+<p>A server layer that sits on top of the IOC database may now register itself
425+as such by calling <tt>dbRegisterServer()</tt> and providing optional routines
426+that other components can use. The initial purpose of this API allows the Trace
427+Processing implementation in <tt>dbProcess()</tt> to identify a client that
428+causes a record to process when TPRO is set.</p>
429+
430+<p>To support the client idenfication, the server provides a routine that
431+returns that identity string when called by one of its own processing
432+threads.</p>
433+
434+<h3>Concatenated database definition files</h3>
435+
436+<p>A series of database definition (dbd) files can now be concatenated during
437+the build process into a newly-created dbd file with result being installed into
438+$(INSTALL_LOCATION)/dbd without expanding it.</p>
439+
440+<p>The following lines in an EPICS Makefile will create a file name.dbd in the
441+O.Common build directory containing the contents of file1.dbd followed by
442+file2.dbd then file3.dbd. The new file will then be installed into
443+$(INSTALL_LOCATION)/dbd without expanding any of its include statements.</p>
444+
445+<blockquote><pre>
446+DBDCAT += name.dbd
447+name_DBD += file1.dbd file2.dbd file3.dbd
448+</pre></blockquote>
449+
450+<p>The source files file1.dbd, file2.dbd and file3.dbd may be created by the
451+current Makefile, be located in the parent directory or any other directory in
452+the SRC_DIRS list, be specified by their full pathname, exist in the install dbd
453+directory, or be found in any dbd directory linked from the application's
454+RELEASE files.</p>
455+
456+<h3>Posix: Drop SCHED_FIFO before exec() in child process</h3>
457+
458+<p>If Base is compiled with <tt>USE_POSIX_THREAD_PRIORITY_SCHEDULING = YES</tt>
459+in configure/CONFIG_SITE or related files, the Posix implementation of the
460+libCom <tt>osiSpawnDetachedProcess()</tt> routine will switch the child process
461+to use the normal SCHED_OTHER (non real-time) scheduler before executing the
462+named executable program. If it needs to use the real-time scheduler the new
463+program can request that for itself.</p>
464+
465+<h3>Posix: Lock all memory when running with FIFO scheduler</h3>
466+
467+<p>On Posix systems, an IOC application's ability to meet timing deadlines is
468+often dependent on its ability to lock part or all of the process's virtual
469+address space into RAM, preventing that memory from being paged to the swap
470+area. This change will attempt to lock the process's virtual address space into
471+RAM if the process has the ability to run threads with different priorities. If
472+unsuccessful, it prints an message to stderr and continues.</p>
473+
474+<p>On Linux, one can grant a process the ability to run threads with different
475+priorities by using the command <code>ulimit -r unlimited</code>. To use the
476+FIFO scheduler for an IOC, use a command like this:</p>
477+
478+<blockquote><pre>chrt -f 1 softIoc -d test.db</pre></blockquote>
479+
480+<p>On Linux, one can grant a process the ability to lock itself into memory
481+using the command <code>ulimit -l unlimited</code>. These limits can also be
482+configured on a per user/per group basis by changing /etc/security/limits.conf
483+or its equivalent.</p>
484+
485+<p>A child process created via fork() normally inherits its parent's resource
486+limits, so a child of a real-time soft-IOC will get its parent's real-time
487+priority and memlock limits. The memory locks themselves however are not
488+inherited by child processes.</p>
489+
490+<h3>Implement EPICS_CAS_INTF_ADDR_LIST in rsrv</h3>
491+
492+<p>The IOC server can now bind to a single IP address (and optional port number)
493+read from the standard environment parameter EPICS_CAS_INTF_ADDR_LIST.
494+Additional addresses included in that parameter after the first will be ignored
495+and a warning message displayed at iocInit time.</p>
496+
497+<h3>alarmString.h deprecated again</h3>
498+
499+<p>The string arrays that provide string versions of the alarm status and
500+severity values have been moved into libCom and the header file that used to
501+instanciate them is no longer required, although a copy is still provided for
502+backwards compatibility reasons. Only the alarm.h header needs to be included
503+now to declare the epicsAlarmSeverityStrings and epicsAlarmConditionStrings
504+arrays.</p>
505+
506+<h3>General purpose thread pool</h3>
507+
508+<p>
509+A general purpose threaded work queue API epicsThreadPool is added.
510+Multiple pools can be created with controlable priority and number
511+of worker threads. Lazy worker startup is supported.</p>
512+
513+<h3>Database field setting updates</h3>
514+
515+<p>A database (.db) file loaded by an IOC does not have to repeat the record
516+type of a record that has already been loaded. It may replace the first
517+parameter of the <tt>record(type, name)</tt> statement with an asterisk
518+character inside double-quotes, <tt>"*"</tt> instead. Thus the following is a
519+legal database file:</p>
520+
521+<blockquote><pre>record(ao, "ao1") {}
522+record("*", "ao1") {
523+ field(VAL, 10)
524+}</pre></blockquote>
525+
526+<p>Note that database configuration tools will not be expected to have to
527+understand this syntax, which is provided for scripted and hand-coded database
528+and template instantiation only. Setting the IOC's <tt>dbRecordsOnceOnly</tt>
529+flag also makes this syntax illegal, since its purpose is to prevent
530+multiply-defined records from being collapsed into a single instance.</p>
531+
532+<h3>Added echo command to iocsh</h3>
533+
534+<p>The single argument string may contain escaped characters, which will be
535+translated to their raw form before being printed (enclose the string in quotes
536+to avoid double-translation). A newline is always appended to the output, and
537+output stream redirection is supported.</p>
538+
539+<h3>Added macro EPICS_UNUSED to compilerDependencies.h</h3>
540+
541+<p>To prevent the compiler from warning about a known-unused variable, mark it
542+with the macro EPICS_UNUSED. On gcc and clang this will expand to
543+<tt>__attribute__((unused))</tt> to prevent the warning.</p>
544+
545+<h3>User specified db substitution file suffix</h3>
546+
547+<p>Per Dirk Zimoch's suggestion, a user specified db substitution file suffix is
548+now allowed by setting the variable SUBST_SUFFIX in a configuration directory
549+CONFIG_SITE file or in a Makefile before the include $(TOP)/configure/RULES
550+line. The default for SUBST_SUFFIX is <tt>.substitutions</tt></p>
551+
552+<h3>NTP Time Provider adjusts to OS tick rate changes</h3>
553+
554+<p>Dirk Zimoch provided code that allows the NTP Time provider (used on VxWorks
555+and RTEMS only) to adapt to changes in the OS clock tick rate after the provider
556+has been initialized. Note that changing the tick rate after iocInit() is not
557+advisable, and that other software might still misbehave if initialized before
558+an OS tick rate change.</p>
559+
560+<h3>Added newEpicsMutex macro</h3>
561+
562+<p>Internal C++ uses of <tt>new epicsMutex()</tt> have been replaced with a new
563+macro which calls a new constructor, passing it the file name and line number of
564+the mutex creation code. C code that creates mutexes has been using a similar
565+macro for a long time, but there was no equivalent constructor for the C++
566+wrapper class, so identifying a specific mutex was much harder to do.</p>
567+
568+<h3>Post DBE_PROPERTY events automatically</h3>
569+
570+<p>A new record field attribute "prop(YES)" has been added to identify fields
571+holding meta-data. External changes to these fields will cause a CA monitor
572+event to be sent to all record subscribers who have asked for DBE_PROPERTY
573+updates. Meta-data fields have been marked for all Base record types.</p>
574+
575+<h3>errlogRemoveListener() routine changed</h3>
576+
577+<p>Code that calls <tt>errlogRemoveListener(myfunc)</tt> must be modified to use
578+the new, safer routine <tt>errlogRemoveListeners(myfunc, &amp;pvt)</tt> instead.
579+The replacement routine takes a second argument which must be the same private
580+pointer that was passed to <tt>errlogAddListener()</tt> when adding that
581+listener. It also deletes all matching listeners (hence the new plural name) and
582+returns how many were actually deleted, whereas the previous routine only
583+removed the first listener that matched.</p>
584+
585+<h3>Simplified generation of .dbd files</h3>
586+
587+<p>The Perl script <tt>makeIncludeDbd.pl</tt> has been removed and the rules
588+that created an intermediate <tt><i>xxx</i>Include.dbd</tt> file from the
589+Makefile variable <tt>xxx_DBD</tt> have been modified to generate the target
590+<tt><i>xxx</i>.dbd</tt> file directly. This should simplify applications that
591+might have had to provide dependency rules for the intermediate files in 3.15.
592+Applications which provide their own <tt><i>xxx</i>Include.dbd</tt> source file
593+will continue to have it expanded as before.</p>
594+
595+<h3>New Undefined Severity field UDFS</h3>
596+
597+<p>A new field has been added to dbCommon which configures the alarm severity
598+associated with the record being undefined (when UDF=TRUE). The default value is
599+INVALID so old databases will not be affected, but now individual records can be
600+configured to have a lower severity or even no alarm when undefined. Be careful
601+when changing this on applications where the IVOA field of output records is
602+used, IVOA still requires an INVALID severity to trigger value replacement.</p>
603+
604+<h3>New build target <q>tapfiles</q></h3>
605+
606+<p>This new make target runs the same tests as the <q>runtests</q> target, but
607+instead of summarizing or displaying the output for each test script it creates
608+a <q>.tap</q> file inside the architecture build directory which contains the
609+detailed test output. The output file can be parsed by continuous integration
610+packages such as <a href="http://www.jenkins-ci.org/">Jenkins</a> to show the
611+test results.</p>
612+
613+<h3>Array field double-buffering</h3>
614+
615+<p>Array data can now be moved, without copying, into and out of the VAL field
616+of the waveform, aai, and aao record types by replacing the pointer in BPTR.
617+The basic rules which device support must follow are:</p>
618+
619+<ol>
620+ <li>BPTR, and the memory it is currently pointing to, can only be accessed
621+ while the record is locked.</li>
622+ <li>NELM may not be changed; NORD should be updated whenever the number of
623+ valid data elements changes.</li>
624+ <li>When BPTR is replaced it must always point to a block of memory large
625+ enough to hold the maximum number of elements, as given by the NELM and
626+ FTVL fields.</li>
627+</ol>
628+
629+<h3>Spin-locks API added</h3>
630+
631+<p>The new header file epicsSpin.h adds a portable spin-locks API which is
632+intended for locking very short sections of code (typically one or two lines of
633+C or C++) to provide a critical section that protects against race conditions.
634+On Posix platforms this uses the pthread_spinlock_t type if it's available and
635+the build is not configured to use Posix thread priorities, but otherwise it
636+falls back to a pthread_mutex_t. On the UP VxWorks and RTEMS platforms the
637+implementations lock out CPU interrupts and disable task preemption while a
638+spin-lock is held. The default implementation (used when no other implementation
639+is provided) uses an epicsMutex. Spin-locks may not be taken recursively, and
640+the code inside the critical section should be short and deterministic.</p>
641+
642+<h3>Improvements to aToIPAddr()</h3>
643+
644+<p>The libCom routine aToIPAddr() and the vxWorks implementation of the
645+associated hostToIPAddr() function have been modified to be able to look up
646+hostnames that begin with one or more digits. The epicsSockResolveTest program
647+was added to check this functionality.</p>
648+
649+<h3>mbboDirect and mbbiDirect records</h3>
650+
651+<p>These record types have undergone some significant rework, and will behave
652+slightly differently than they did in their 3.14 versions. The externally
653+visible changes are as follows:</p>
654+
655+<h5>mbbiDirect</h5>
656+
657+<ul>
658+ <li>If the MASK field is set in a database file, it will not be over-written
659+ when the record is initialized. This allows non-contiguous masks to be set,
660+ although only the device support actually uses the MASK field.</li>
661+ <li>If process() finds the UDF field to be set, the record will raise a
662+ UDF/INVALID alarm.</li>
663+</ul>
664+
665+<h5>mbboDirect</h5>
666+
667+<ul>
668+ <li>If the MASK field is set in a database file, it will not be over-written
669+ when the record is initialized. This allows non-contiguous masks to be set,
670+ although only the device support actually uses the MASK field.</li>
671+ <li>After the device support's init_record() routine returns during record
672+ initialization, if OMSL is <q>supervisory</q> and UDF is clear the fields
673+ B0-BF will be set from the current VAL field.</li>
674+ <li>When a put to the OMSL field sets it to <q>supervisory</q>, the fields
675+ B0-BF will be set from the current VAL field. This did not used to happen,
676+ the individual bit fields were previously never modified by the record.
677+ Note that this change may require some databases to be modified, if they
678+ were designed to take advantage of the previous behavior.</li>
679+</ul>
680+
681+<h3>Redirection of the errlog console stream</h3>
682+
683+<p>A new routine has been added to the errlog facility which allows the console
684+error message stream to be redirected from stderr to some other already open
685+file stream:</p>
686+
687+<blockquote><pre>int errlogSetConsole(FILE *stream);
688+</pre></blockquote>
689+
690+<p>The stream argument must be a FILE* pointer as returned by fopen() that is
691+open for output. If NULL is passed in, the errlog thread's stderr output stream
692+will be used instead. Note that messages to the console can be disabled and
693+re-enabled using the eltc routine which is also an iocsh command, but there is
694+no iocsh command currently provided for calling errlogSetConsole.</p>
695+
696+<h3>Add cleanup subroutine to aSub record</h3>
697+
698+<p>An aSub routine may set the CADR field with a function pointer which will be
699+run before a new routine in the event that a change to the SNAM field changes
700+the record's process subroutine.</p>
701+
702+<p>This can be used to free any resources the routine needs to allocate. It can
703+also be used to determine if this is the first time this routine has been called
704+by this record instance. The CADR field is set to NULL immediately after the
705+routine it points to is called.</p>
706+
707+<p>Example:</p>
708+
709+<blockquote><pre>void cleanup(aSubRecord* prec) {
710+ free(prec-&gt;dpvt);
711+ prec-&gt;dpvt = NULL;
712+}
713+
714+long myAsubRoutine(aSubRecord* prec) {
715+ if (!prec-&gt;cadr) {
716+ /* check types of inputs and outputs */
717+ if (prec-&gt;ftva != menuFtypeDOUBLE)
718+ return 1; /* oops */
719+
720+ dpvt = malloc(42);
721+ prec-&gt;cadr = &amp;cleanup;
722+ }
723+
724+ /* normal processing */
725+}
726+epicsRegisterFunction(myAsubRoutine);
727+</pre></blockquote>
728+
729+<h3>Sequence record enhancements</h3>
730+
731+<p>The sequence record type now has 16 link groups numbered 0 through 9 and A
732+through F, instead of the previous 10 groups numbered 1 through 9 and A. The
733+changes to this record are directly equivalent to those described below for the
734+fanout record. The fields OFFS and SHFT have been added and operate on the SELN
735+value exactly the same way. The result is backwards compatible with the 3.14
736+version of the sequence record as long as none of the new fields are modified
737+and the application does not rely on the SOFT/INVALID alarm that was generated
738+when the selection number exceeded 10. The record also now posts monitors on the
739+SELN field at the end of the sequence if its value changed when read through the
740+SELL link.
741+
742+<h3>Fanout record enhancements</h3>
743+
744+<p>The fanout record type now has 16 output links LNK0-LNK9 and LNKA-LNKF, plus
745+two additional fields which make the result backwards compatible with 3.14
746+databases, but also allow the link selection to be shifted without having to
747+process the SELN value through a calc or calcout record first.</p>
748+
749+<p>Previously there was no LNK0 field, so when SELM is <q>Mask</q> bit 0 of SELN
750+controls whether the LNK1 link field was activated; bit 1 controls LNK2 and so
751+on. When SELM is <q>Specified</q> and SELN is zero no output link would be
752+activated at all; LNK1 gets activated when SELN is 1 and so on. Only 6 links
753+were provided, LNK1 through LNK6. The updated record type maintains the original
754+behavior when the new fields are not configured, except that the SOFT/INVALID
755+alarm is not generated when SELN is 7 through 15.</p>
756+
757+<p>The update involved adding a LNK0 field, as well as fields LNK7 through LNK9
758+and LNKA through LNKF. To add flexibility and maintain backwards compatibility,
759+two additional fields have been added:</p>
760+
761+<dl>
762+<dt><b>OFFS</b></dt>
763+
764+<dd>This field holds a signed offset which is added to SELN to select which link
765+to activate when SELM is <q>Specified</q>. If the resulting value is outside the
766+range 0 .. 15 the record will go into a SOFT/INVALID alarm state. The default
767+value of OFFS is zero, so if it is not explicitly set and SELN is 1 the LNK1
768+link will be activated.</dd>
769+
770+<dt><b>SHFT</b></dt>
771+
772+<dd>When SELM is <q>Mask</q> the signed field SHFT is used to shift the SELN
773+value by SHFT bits (positive means right-wards, values outside the range -15 ..
774+15 will result in a SOFT/INVALID alarm), before using the resulting bit-pattern
775+to control which links to activate. The default value is -1, so if SHFT is not
776+explicitly set bit 0 of SELN will be used to control whether LNK1 gets
777+activated.</dd>
778+
779+</dl>
780+
781+<p>The record also now posts monitors on the SELN field if it changes as a
782+result of record processing (i.e. when read through the SELL link).</p>
783+
784+<h3>Deleted Java build rules</h3>
785+
786+<p>Java has its own build systems now, so we've deleted the rules and associated
787+variables from Base, although they might get added to the Extensions build rules
788+for a while in case anyone still needs them.</p>
789+
790+<h2 align="center">Changes between 3.14.x and 3.15.0.1</h2>
791+
792+<h3>Application clean rules</h3>
793+
794+<p>The <tt>clean</tt> Makefile target has changed between a single-colon rule
795+and a double-colon rule more than once in the life of the EPICS build rules, and
796+it just changed back to a single-colon rule, but now we recommend that
797+applications that wish to provide a Makefile that is backwards compatible with
798+the 3.14 build rules use the construct shown below. The 3.15 rules now support
799+a variable called <tt>CLEANS</tt> to which a Makefile can add a list of files to
800+be deleted when the user does a <tt>make clean</tt> like this:</p>
801+
802+<blockquote><pre>CLEANS += &lt;list of files to be cleaned&gt;
803+
804+ifndef BASE_3_15
805+clean::
806+ $(RM) $(CLEANS)
807+endif</pre></blockquote>
808+
809+<p>The conditional rule provides compatibility for use with the 3.14 build
810+system.</p>
811+
812+<h3>MSI included with Base</h3>
813+
814+<p>An enhanced version of the Macro Substitution and Include program <q>msi</q>
815+has been included with Base. Both this new version of msi and the IOC's
816+<tt>dbLoadTemplates</tt> command now support setting global macros in
817+substitution files, and <tt>dbLoadTemplates</tt> can now take a list of global
818+macro settings as the second argument on its command line. The substitution file
819+syntax is documented in the Application Developers Guide.</p>
820+
821+<h3>Cross-builds targeting win32-x86-mingw</h3>
822+
823+<p>Some Linux distributions now package the MinGW cross-compiler which makes it
824+possible to cross-build the win32-x86-mingw target from a linux-x86 host. Build
825+configuration files for this combination are now included; adjust the settings
826+in configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw and add win32-x86-mingw to
827+the CROSS_COMPILER_TARGET_ARCHS variable in configure/CONFIG_SITE or in
828+configure/os/CONFIG_SITE.linux-x86.Common.</p>
829+
830+<h3>Architecture win32-x86-cygwin Removed</h3>
831+
832+<p>The ability to compile non-cygwin binaries using the Cygwin build tools is no
833+longer supported by current versions of Cygwin, so this architecture has been
834+removed. Use the MinWG tools and the win32-x86-mingw architecture instead.</p>
835+
836+<h3>RTEMS and VxWorks Test Harnesses</h3>
837+
838+<p>The original libCom test harness has been renamed <tt>libComTestHarness</tt>,
839+and two additional test harnesses have been created <tt>dbTestHarness</tt> and
840+<tt>filterTestHarness</tt> which are all built for RTEMS and vxWorks targets.
841+The new ones include tests in src/ioc/db/test and src/std/filters/test.</p>
842+
843+<p>Running the new tests requires additional .db and .dbd files to be loaded at
844+runtime, which can be found in the relevant source directory or its O.Common
845+subdirectory. If the target can access the Base source tree directly it may be
846+simplest to cd to the relevant source directory before running the test. If not,
847+the files needed are listed in the generated 'testspec' file found in the
848+associated build (O.<i>arch</i>) directory.</p>
849+
850+<p>For RTEMS users the current directory is determined in a BSP specific way.
851+See rtems_init.c and setBootConfigFromNVRAM.c in src/libCom/RTEMS.</p>
852+
853+<h3>New API to hook into thread creation</h3>
854+
855+<p>A hook API has been added allowing user-supplied functions to be called
856+whenever a thread starts. The calls are made from the thread's context,
857+and can be used to control additional thread properties not handled inside
858+EPICS base, e.g. setting the scheduling policy or CPU affinity (on SMP
859+systems).</p>
860+
861+<p>The API also supports a mapping operation, calling a user-supplied function
862+for every thread that is currently running.</p>
863+
864+<h3>New scan rate units</h3>
865+
866+<p>Scan rates defined in the menuScan.dbd file may now be specified in seconds,
867+minutes, hours or Hertz, and plural time units will also be accepted (seconds
868+are used if no unit is mentioned in the choice string). At <tt>iocInit</tt> each
869+scan rate is compared with the OS's clock tick and a warning printed if the
870+rate is too fast or likely to be more than 10% different to the requested rate.
871+For example the rates given below are all valid, although non-standard (the
872+default menuScan choices that come with Base have not been changed):</p>
873+
874+<blockquote>
875+<pre>menu(menuScan) {
876+ choice(menuScanPassive, "Passive")
877+ choice(menuScanEvent, "Event")
878+ choice(menuScanI_O_Intr, "I/O Intr")
879+ choice(menuScan1_hour, "1 hour")
880+ choice(menuScan0_5_hours, "0.5 hours")
881+ choice(menuScan15_minutes, "15 minutes")
882+ choice(menuScan5_minutes, "5 minutes")
883+ choice(menuScan1_minute, "1 minute")
884+ choice(menuScan10_seconds, "10 seconds")
885+ choice(menuScan5_seconds, "5 seconds")
886+ choice(menuScan2_seconds, "2 seconds")
887+ choice(menuScan1_second, "1 second")
888+ choice(menuScan2_Hertz, "2 Hertz")
889+ choice(menuScan5_Hertz, "5 Hertz")
890+ choice(menuScan10_Hertz, "10 Hz")
891+}</pre></blockquote>
892+
893+<h3>Alarm filtering added to input record types</h3>
894+
895+<p>The record types ai, calc, longin and mbbi have a new alarm filter added to
896+them. This provides a low-pass filter that can be used to delay the reporting of
897+alarms caused by the input level passing the HIGH, HIHI, LOW or LOLO values. The
898+filter is controlled with a new AFTC field that sets the filter's time constant.
899+The default value for this field is zero, which keeps the record's original
900+alarm behaviour.</p>
901+
902+<p>The record must be scanned often enough for the filtering action to work
903+effectively and the alarm severity can only change when the record is processed,
904+but that processing does not have to be regular; the filter uses the time since
905+the record last processed in its calculation. Setting AFTC to a positive number
906+of seconds will delay the record going into or out of a minor alarm severity or
907+from minor to major severity until the input signal has been in that range for
908+that number of seconds.</p>
909+
910+<h3>Post events on Waveform record's NORD field</h3>
911+
912+<p>When the record type or device support modify the NORD field of a waveform
913+record, the record support code now posts DBE_VALUE and DBE_LOG events for that
914+field, signalling the array length change to any client monitoring the NORD
915+field.</p>
916+
917+<h3>Attributes of Non-VAL Fields</h3>
918+
919+<p>Non-VAL fields now report meaningful information for precision, units,
920+graphic limits, control limits, and alarm limits instead of simply using
921+PREC, EGU, HOPR, LOPR, DRVL, DRVH, HIHI, HIGH, LOW, and LOLO. All delay
922+fields have a default precision of 2 digits, units "s" and control limits
923+of 0 to 100,000 seconds (these precision and limit values can be changed
924+for each record type as a whole at runtime by updating a registered global
925+variable). Input fields like A-L of the calc record read their metadata
926+from the corresponding INPn link if possible.</p>
927+<h4>epicsStdioRedirect.h merged into epicsStdio.h</h4>
928+
929+<p>The definitions from the header file epicsStdioRedirect.h have been moved
930+into epicsStdio.h so all calls to printf(), puts() and putchar() in files that
931+include that OSI header will now be subject to stdout redirection. In past
932+releases (3.14.7 and later) it was necessary to request the redirection support
933+by including the epicsStdioRedirect.h header file. The header file is still
934+provided, but now it just includes epicsStdio.h.</p>
935+
936+<h4>Named Soft Events</h4>
937+
938+<p>Soft events can now be given meaningful names instead of just using the
939+numbers 1-255. The EVNT field is now a DBF_STRING. The <tt>post_event()</tt> API
940+is now deprecated but still works. It should be replaced by code that in advance
941+looks up the <tt>EVNTPVT</tt> event handle associated with the named event by
942+calling <tt>eventNameToHandle(char *)</tt>, and when that event occurs passes
943+that handle to the new <tt>postEvent(EVNTPVT)</tt> routine (which may be called
944+from interrupt level). A new iocsh command <tt>postEvent <i>name</i></tt> will
945+trigger a named event from the command-line or a startup script (on vxWorks the
946+expression <tt>postEvent(eventNameToHandle("<i>name</i>"))</tt> must be used
947+instead though).</p>
948+
949+<h4>Parallel Builds</h4>
950+
951+<p>
952+As EPICS sites get computers with more CPUs they report additional bugs in our
953+parallel build rules. Various issues have been fixed by separating out the build
954+rules that generate dependency (.d) files, ensuring that they are constructed at
955+the appropriate time in the build.</p>
956+
957+<p>
958+These rule changes can cause additional warning messages to appear when building
959+support modules. Where an application provides its own Makefile rules it may now
960+have to add rules to construct an associated dependency file. In many cases
961+though the change needed is just to replace a dependency for a
962+<tt>target$(OBJ)</tt> with the <tt>target$(DEP)</tt> so this</p>
963+
964+<pre>
965+ myLib$(OBJ): myLib_lex.c</pre>
966+
967+<p>
968+becomes</p>
969+
970+<pre>
971+ myLib$(DEP): myLib_lex.c</pre>
972+
973+<p>
974+To debug build issues assocated with dependency files, use the command <tt>make
975+--debug=m</tt> which tells GNUmake to display information about what it is doing
976+during the first pass when it updates its makefiles.</p>
977+
978+<h3>
979+Removed tsDefs.h</h3>
980+
981+<p>
982+The deprecated tsDefs API was provided for 3.13 compatibility only, and has now
983+been removed. Convert any remaining code that used it to call the epicsTime API
984+instead.</p>
985+
986+<h3>
987+Changes to epicsVersion.h</h3>
988+
989+<p>
990+The two macros <tt>EPICS_UPDATE_LEVEL</tt> and <tt>EPICS_CVS_SNAPSHOT</tt> have
991+been deleted from the epicsVersion.h file; they were deprecated in R3.14 and can
992+be replaced with <tt>EPICS_PATCH_LEVEL</tt> and <tt>EPICS_DEV_SNAPSHOT</tt>
993+respectively.</p>
994+
995+<p>
996+A new pair of macros has been added to make version number comparisons easier.
997+Code that will not work with a version of Base before 3.15.0 can now be
998+written like this to prevent it from compiling:</p>
999+
1000+<pre style="margin: 0 2em;">
1001+#if defined(VERSION_INT) &amp;&amp; EPICS_VERSION_INT &lt; VERSION_INT(3,15,0,0)
1002+# error EPICS Base R3.15.0 or later is required
1003+#endif
1004+</pre>
1005+
1006+<h3>
1007+Added support for iocLogPrefix</h3>
1008+
1009+<p>
1010+Added a <code>iocLogPrefix</code> command to <code>iocsh</code>. This adds a
1011+prefix to all messages from this IOC (or other log client) as they get sent to the
1012+iocLogServer. This lets sites use the "fac=&lt;<i>facility</i>&gt;" syntax for
1013+displaying the facility, process name etc. in log viewers like the
1014+<code>cmlogviewer</code>.</p>
1015+
1016+<h3>
1017+Reworked the epicsEvent C &amp; C++ APIs</h3>
1018+
1019+<ul>
1020+ <li>Renamed the enum epicsEventWaitStatus to epicsEventStatus</li>
1021+ <li>Defined epicsEventWaitStatus as a macro for epicsEventStatus</li>
1022+ <li>Renamed epicsEventWaitOk to epicsEventOk</li>
1023+ <li>Renamed epicsEventWaitError to epicsEventError</li>
1024+ <li>Defined epicsEventWaitOK and epicsEventWaitError as macros</li>
1025+ <li>Added epicsEventTrigger(id) which triggers an event and returns OK or an
1026+ error status if the underlying OS primitives report an error</li>
1027+ <li>Added epicsEventMustTrigger(id) which halts on error</li>
1028+ <li>Defined epicsEventSignal(id) as a macro for epicsEventMustTrigger(id)</li>
1029+ <li>Added a new C++ method epicsEvent::trigger() which throws an
1030+ epicsEvent::invalidSemaphore in the event of an error</li>
1031+ <li>epicsEvent::signal() makes an inline call to epicsEvent::trigger()</li>
1032+ <li>epicsEventWait() and epicsEventWaitWithTimeout() now return an error
1033+ status if the underlying OS primitives report an error</li>
1034+ <li>All the epicsEventMust...() routines are now implemented in the common
1035+ libCom/osi/epicsEvent.cpp source file, and call cantProceed() instead of
1036+ mis-using assert()</li>
1037+ <li>Implemented epicsEventShow() on Posix</li>
1038+ <li>Win32: Removed all epicsShareAPI decorations</li>
1039+</ul>
1040+
1041+<h3>
1042+Enabled histogram record type</h3>
1043+
1044+<p>
1045+The histogram record was not included in the base.dbd file in any 3.14 release,
1046+but has now been added along with its associated soft device support. The build
1047+system now generates the list of all the record.dbd files in base automatically
1048+in src/std/rec/Makefile.</p>
1049+
1050+<h3>
1051+Reorganization of src/</h3>
1052+
1053+<p>Reorganization of subdirectories of src/ to better represent the relation
1054+between different parts as described in the following table.</p>
1055+
1056+<p>This change also allows the number of libraries built to be reduced to:
1057+libCap5.so, libca.so, libdbCore.so, libdbStaticHost.so,
1058+libCom.so, libcas.so, libdbRecStd.so, and libgdd.so</p>
1059+
1060+<table border="1"><tbody>
1061+<tr>
1062+ <th>Component</th>
1063+ <th>Dependency</th>
1064+ <th>Library name</th>
1065+ <th>Description</th>
1066+</tr>
1067+<tr>
1068+ <td>src/tools</td>
1069+ <td></td>
1070+ <td></td>
1071+ <td>Build system scripts</td>
1072+</tr>
1073+<tr>
1074+ <td>src/libCom</td>
1075+ <td>src/tools</td>
1076+ <td>Com</td>
1077+ <td>Utility routines and OS-independant API</td>
1078+</tr>
1079+<tr>
1080+ <td>src/template</td>
1081+ <td>src/tools</td>
1082+ <td></td>
1083+ <td>User application templates (e.g. makeBaseApp)</td>
1084+</tr>
1085+<tr>
1086+ <td>src/ca/client</td>
1087+ <td>src/libCom</td>
1088+ <td>ca</td>
1089+ <td>Channel Access client</td>
1090+</tr>
1091+<tr>
1092+ <td>src/ca/legacy/gdd</td>
1093+ <td>src/ca/client</td>
1094+ <td>gdd</td>
1095+ <td>Generic data layer for PCAS</td>
1096+</tr>
1097+<tr>
1098+ <td>src/ca/legacy/pcas</td>
1099+ <td>src/ca/legacy/gdd</td>
1100+ <td>cas</td>
1101+ <td>Portable Channel Access Server</td>
1102+</tr>
1103+<tr>
1104+ <td>src/ioc</td>
1105+ <td>src/ca</td>
1106+ <td>dbCore</td>
1107+ <td>Core database processing functions</td>
1108+</tr>
1109+<tr>
1110+ <td>src/std</td>
1111+ <td>src/ioc</td>
1112+ <td>dbRecStd</td>
1113+ <td>Standard records, soft device support and the softIoc </td>
1114+</tr>
1115+</tbody></table>
1116+
1117+<p>
1118+In order to better reflect these relations the following
1119+directories and files were moved as described:</p>
1120+
1121+<table border="1"><tbody>
1122+<tr>
1123+ <th colspan="2">Relocations</th>
1124+</tr>
1125+<tr>
1126+ <th>Previous</th><th>New</th>
1127+</tr>
1128+<tr>
1129+ <th colspan="2">libCom</th>
1130+</tr>
1131+<tr>
1132+ <td>src/RTEMS</td>
1133+ <td>src/libCom/RTEMS</td>
1134+</tr>
1135+<tr>
1136+ <td>src/toolsComm/flex</td>
1137+ <td>src/libCom/flex</td>
1138+</tr>
1139+<tr>
1140+ <td>src/toolsComm/antelope</td>
1141+ <td>src/libCom/yacc</td>
1142+</tr>
1143+<tr>
1144+ <td align="right">src/dbStatic/alarm.h<br>.../alarmString.h</td>
1145+ <td>src/libCom/misc/</td>
1146+</tr>
1147+<tr>
1148+ <th colspan="2">IOC Core Components</th>
1149+</tr>
1150+<tr>
1151+ <td>src/bpt</td>
1152+ <td>src/ioc/bpt</td>
1153+</tr>
1154+<tr>
1155+ <td>src/db</td>
1156+ <td>src/ioc/db</td>
1157+</tr>
1158+<tr>
1159+ <td>src/dbStatic</td>
1160+ <td>src/ioc/dbStatic</td>
1161+</tr>
1162+<tr>
1163+ <td>src/dbtools</td>
1164+ <td>src/ioc/dbtemplate</td>
1165+</tr>
1166+<tr>
1167+ <td>src/misc</td>
1168+ <td>src/ioc/misc</td>
1169+</tr>
1170+<tr>
1171+ <td>src/registry</td>
1172+ <td>src/ioc/registry</td>
1173+</tr>
1174+<tr>
1175+ <td>src/rsrv</td>
1176+ <td>src/ioc/rsrv <a href="#rsrv">1</a></td>
1177+</tr>
1178+<tr>
1179+ <th colspan="2">Standard Record Definitions</th>
1180+</tr>
1181+<tr>
1182+ <td>src/dev/softDev</td>
1183+ <td>src/std/dev</td>
1184+</tr>
1185+<tr>
1186+ <td>src/rec</td>
1187+ <td>src/std/rec</td>
1188+</tr>
1189+<tr>
1190+ <td>src/softIoc</td>
1191+ <td>src/std/softIoc</td>
1192+</tr>
1193+<tr>
1194+ <th colspan="2">Channel Access</th>
1195+</tr>
1196+<tr>
1197+ <td>src/ca</td>
1198+ <td>src/ca/client</td>
1199+</tr>
1200+<tr>
1201+ <td>src/catools</td>
1202+ <td>src/ca/client/tools</td>
1203+</tr>
1204+<tr>
1205+ <td>src/cap5</td>
1206+ <td>src/ca/client/perl</td>
1207+</tr>
1208+<tr>
1209+ <td>src/gdd</td>
1210+ <td>src/ca/legacy/gdd</td>
1211+</tr>
1212+<tr>
1213+ <td>src/cas</td>
1214+ <td>src/ca/legacy/pcas</td>
1215+</tr>
1216+<tr>
1217+ <td>src/excas</td>
1218+ <td>src/ca/legacy/pcas/ex</td>
1219+</tr>
1220+<tr>
1221+ <th colspan="2">User Templates</th>
1222+</tr>
1223+<tr>
1224+ <td>src/makeBaseApp</td>
1225+ <td>src/template/base</td>
1226+</tr>
1227+<tr>
1228+ <td>src/makeBaseExt</td>
1229+ <td>src/template/ext</td>
1230+</tr>
1231+<tr>
1232+ <th colspan="2">Dispersed</th>
1233+</tr>
1234+<tr>
1235+ <td rowspan="3">src/util <a href="#util">2</a></td>
1236+ <td>src/ca/client</td>
1237+</tr>
1238+<tr>
1239+ <td>src/ca/client/test</td>
1240+</tr>
1241+<tr>
1242+ <td>src/libCom/log</td>
1243+</tr>
1244+<tr>
1245+ <td rowspan="2">src/as <a href="#as">3</a></td>
1246+ <td>src/libCom/as</td>
1247+</tr>
1248+<tr>
1249+ <td>src/ioc/as</td>
1250+</tr>
1251+</tbody></table>
1252+
1253+<p><a name="rsrv">1</a>
1254+RSRV is built as part of dbCore due to its tight (bidirectional) coupling
1255+with the other database code.</p>
1256+
1257+<p><a name="util">2</a>
1258+The contents for src/util/ moved to three locations. The caRepeater init script
1259+was moved to src/ca/client/. ca_test is now in src/ca/client/test/.
1260+The iocLogServer was moved into the same directory (src/libCom/log) as
1261+the log client code.</p>
1262+
1263+<p><a name="as">3</a>
1264+The Access Security code has been divided, with the parts not related to the
1265+database (lexer/parser and trap registration) becoming part of libCom.
1266+The remaining components are included in the dbCore library</p>
1267+
1268+<h3>
1269+Moved src/RTEMS/base directory</h3>
1270+
1271+<p>
1272+These files are now found under src/RTEMS.</p>
1273+
1274+<h3>
1275+Removed 3.13 compatibility</h3>
1276+
1277+<p>
1278+Removed the 3.13 &lt;top&gt;/config directory and build compatibility rules and
1279+variables, and various conversion documents.</p>
1280+>>>>>>> MERGE-SOURCE
1281
1282 </body>
1283 </html>
1284
1285=== modified file 'src/ca/client/addrList.h'
1286--- src/ca/client/addrList.h 2008-09-19 23:27:52 +0000
1287+++ src/ca/client/addrList.h 2016-01-12 02:07:47 +0000
1288@@ -22,7 +22,7 @@
1289 epicsShareFunc void epicsShareAPI configureChannelAccessAddressList
1290 ( struct ELLLIST *pList, SOCKET sock, unsigned short port );
1291
1292-epicsShareFunc void epicsShareAPI addAddrToChannelAccessAddressList
1293+epicsShareFunc int epicsShareAPI addAddrToChannelAccessAddressList
1294 ( struct ELLLIST *pList, const ENV_PARAM *pEnv,
1295 unsigned short port, int ignoreNonDefaultPort );
1296
1297
1298=== modified file 'src/ca/client/iocinf.cpp'
1299--- src/ca/client/iocinf.cpp 2013-12-17 18:54:04 +0000
1300+++ src/ca/client/iocinf.cpp 2016-01-12 02:07:47 +0000
1301@@ -73,7 +73,7 @@
1302 /*
1303 * addAddrToChannelAccessAddressList ()
1304 */
1305-extern "C" void epicsShareAPI addAddrToChannelAccessAddressList
1306+extern "C" int epicsShareAPI addAddrToChannelAccessAddressList
1307 ( ELLLIST *pList, const ENV_PARAM *pEnv,
1308 unsigned short port, int ignoreNonDefaultPort )
1309 {
1310@@ -82,11 +82,11 @@
1311 const char *pToken;
1312 struct sockaddr_in addr;
1313 char buf[32u]; /* large enough to hold an IP address */
1314- int status;
1315+ int status, ret = -1;
1316
1317 pStr = envGetConfigParamPtr (pEnv);
1318 if (!pStr) {
1319- return;
1320+ return ret;
1321 }
1322
1323 while ( ( pToken = getToken (&pStr, buf, sizeof (buf) ) ) ) {
1324@@ -104,7 +104,7 @@
1325 pNewNode = (osiSockAddrNode *) calloc (1, sizeof(*pNewNode));
1326 if (pNewNode==NULL) {
1327 fprintf ( stderr, "addAddrToChannelAccessAddressList(): no memory available for configuration\n");
1328- return;
1329+ break;
1330 }
1331
1332 pNewNode->addr.ia = addr;
1333@@ -113,9 +113,10 @@
1334 * LOCK applied externally
1335 */
1336 ellAdd (pList, &pNewNode->node);
1337+ ret = 0; /* success if anything is added to the list */
1338 }
1339
1340- return;
1341+ return ret;
1342 }
1343
1344 /*
1345
1346=== modified file 'src/ca/client/udpiiu.cpp'
1347--- src/ca/client/udpiiu.cpp 2015-08-18 18:33:44 +0000
1348+++ src/ca/client/udpiiu.cpp 2016-01-12 02:07:47 +0000
1349@@ -163,6 +163,19 @@
1350 throwWithLocation ( noSocket () );
1351 }
1352
1353+#ifdef IP_ADD_MEMBERSHIP
1354+ {
1355+ int flag = 1;
1356+ if(setsockopt(this->sock, IPPROTO_IP, IP_MULTICAST_LOOP, &flag, sizeof(flag))==-1)
1357+ {
1358+ char sockErrBuf[64];
1359+ epicsSocketConvertErrnoToString (
1360+ sockErrBuf, sizeof ( sockErrBuf ) );
1361+ errlogPrintf("CAC: failed to set mcast loopback\n");
1362+ }
1363+ }
1364+#endif
1365+
1366 int boolValue = true;
1367 int status = setsockopt ( this->sock, SOL_SOCKET, SO_BROADCAST,
1368 (char *) &boolValue, sizeof ( boolValue ) );
1369
1370=== modified file 'src/ioc/dbStatic/dbLex.l'
1371--- src/ioc/dbStatic/dbLex.l 2009-01-09 18:00:00 +0000
1372+++ src/ioc/dbStatic/dbLex.l 2016-01-12 02:07:47 +0000
1373@@ -73,15 +73,20 @@
1374 {whitespace} ;
1375
1376 {doublequote}({stringchar}|{escape})*{newline} { /* bad string */
1377- yyerror("Newline in string, closing quote missing");
1378+ yyerrorAbort("Newline in string, closing quote missing");
1379 }
1380
1381 . {
1382 char message[40];
1383 YY_BUFFER_STATE *dummy=0;
1384
1385- sprintf(message,"Invalid character '%c'",yytext[0]);
1386- yyerror(message);
1387+ if (isprint((int) yytext[0])) {
1388+ sprintf(message, "Invalid character '%c'", yytext[0]);
1389+ }
1390+ else {
1391+ sprintf(message, "Invalid character 0x%2.2x", yytext[0]);
1392+ }
1393+ yyerrorAbort(message);
1394 /*The following suppresses compiler warning messages*/
1395 if(FALSE) yyunput('c',(unsigned char *) message);
1396 if(FALSE) yy_switch_to_buffer(*dummy);
1397
1398=== modified file 'src/ioc/dbStatic/dbLexRoutines.c'
1399--- src/ioc/dbStatic/dbLexRoutines.c 2015-04-08 22:37:12 +0000
1400+++ src/ioc/dbStatic/dbLexRoutines.c 2016-01-12 02:07:47 +0000
1401@@ -12,6 +12,7 @@
1402
1403 /*The routines in this module are serially reusable NOT reentrant*/
1404
1405+#include <ctype.h>
1406 #include <epicsStdlib.h>
1407 #include <stddef.h>
1408 #include <stdio.h>
1409
1410=== modified file 'src/ioc/dbStatic/dbStaticLib.c'
1411--- src/ioc/dbStatic/dbStaticLib.c 2015-08-20 16:24:07 +0000
1412+++ src/ioc/dbStatic/dbStaticLib.c 2016-01-12 02:07:47 +0000
1413@@ -477,7 +477,10 @@
1414
1415 void dbFreeEntry(DBENTRY *pdbentry)
1416 {
1417- if(pdbentry->message) free((void *)pdbentry->message);
1418+ if (!pdbentry)
1419+ return;
1420+ if (pdbentry->message)
1421+ free((void *)pdbentry->message);
1422 dbmfFree(pdbentry);
1423 }
1424
1425
1426=== modified file 'src/ioc/rsrv/camessage.c'
1427--- src/ioc/rsrv/camessage.c 2015-04-22 21:51:31 +0000
1428+++ src/ioc/rsrv/camessage.c 2016-01-12 02:07:47 +0000
1429@@ -1126,7 +1126,7 @@
1430 pclient = pciu->client;
1431 assert(pclient);
1432
1433- if(pclient == prsrv_cast_client){
1434+ if(pclient->proto==IPPROTO_UDP){
1435 return;
1436 }
1437
1438@@ -1193,7 +1193,7 @@
1439 int v41;
1440 int status;
1441
1442- assert ( pciu->client != prsrv_cast_client );
1443+ assert ( pciu->client->proto!=IPPROTO_UDP );
1444
1445 /*
1446 * noop if this is an old client
1447@@ -1321,7 +1321,7 @@
1448 }
1449 }
1450 else {
1451- epicsMutexMustLock(prsrv_cast_client->chanListLock);
1452+ epicsMutexMustLock(client->chanListLock);
1453 /*
1454 * clients which dont claim their
1455 * channel in use block prior to
1456@@ -1331,7 +1331,7 @@
1457 if(!pciu){
1458 errlogPrintf("CAS: client timeout disconnect id=%d\n",
1459 mp->m_cid);
1460- epicsMutexUnlock(prsrv_cast_client->chanListLock);
1461+ epicsMutexUnlock(client->chanListLock);
1462 SEND_LOCK(client);
1463 send_err(
1464 mp,
1465@@ -1343,33 +1343,15 @@
1466 }
1467
1468 /*
1469- * duplicate claim message are unacceptable
1470- * (so we disconnect the client)
1471- */
1472- if (pciu->client!=prsrv_cast_client) {
1473- errlogPrintf("CAS: duplicate claim disconnect id=%d\n",
1474- mp->m_cid);
1475- epicsMutexUnlock(prsrv_cast_client->chanListLock);
1476- SEND_LOCK(client);
1477- send_err(
1478- mp,
1479- ECA_INTERNAL,
1480- client,
1481- "duplicate claim in old connect protocol");
1482- SEND_UNLOCK(client);
1483- return RSRV_ERROR;
1484- }
1485-
1486- /*
1487 * remove channel in use block from
1488 * the UDP client where it could time
1489 * out and place it on the client
1490 * who is claiming it
1491 */
1492 ellDelete(
1493- &prsrv_cast_client->chanList,
1494+ &client->chanList,
1495 &pciu->node);
1496- epicsMutexUnlock(prsrv_cast_client->chanListLock);
1497+ epicsMutexUnlock(client->chanListLock);
1498
1499 epicsMutexMustLock(client->chanListLock);
1500 pciu->state = rsrvCS_pendConnectResp;
1501@@ -2515,12 +2497,7 @@
1502 unsigned bytes_left;
1503 int status = RSRV_ERROR;
1504
1505- if ( ! pCaBucket ) {
1506- pCaBucket = bucketCreate(CAS_HASH_TABLE_SIZE);
1507- if(!pCaBucket){
1508- return RSRV_ERROR;
1509- }
1510- }
1511+ assert(pCaBucket);
1512
1513 /* drain remnents of large messages that will not fit */
1514 if ( client->recvBytesToDrain ) {
1515@@ -2623,7 +2600,7 @@
1516 if ( CASDEBUG > 2 )
1517 log_header (NULL, client, &msg, pBody, nmsg);
1518
1519- if ( client == prsrv_cast_client ) {
1520+ if ( client->proto==IPPROTO_UDP ) {
1521 if ( msg.m_cmmd < NELEMENTS ( udpJumpTable ) ) {
1522 status = ( *udpJumpTable[msg.m_cmmd] )( &msg, pBody, client );
1523 if (status!=RSRV_OK) {
1524
1525=== modified file 'src/ioc/rsrv/caserverio.c'
1526--- src/ioc/rsrv/caserverio.c 2015-02-28 00:11:37 +0000
1527+++ src/ioc/rsrv/caserverio.c 2016-01-12 02:07:47 +0000
1528@@ -222,7 +222,7 @@
1529 /*
1530 * add placeholder for the first version message should it be needed
1531 */
1532- rsrv_version_reply ( prsrv_cast_client );
1533+ rsrv_version_reply ( pclient );
1534
1535 SEND_UNLOCK(pclient);
1536
1537
1538=== modified file 'src/ioc/rsrv/caservertask.c'
1539--- src/ioc/rsrv/caservertask.c 2015-02-28 00:11:37 +0000
1540+++ src/ioc/rsrv/caservertask.c 2016-01-12 02:07:47 +0000
1541@@ -32,6 +32,7 @@
1542 #include "osiPoolStatus.h"
1543 #include "osiSock.h"
1544 #include "taskwd.h"
1545+#include "cantProceed.h"
1546
1547 #define epicsExportSharedSymbols
1548 #include "dbChannel.h"
1549@@ -58,113 +59,12 @@
1550 */
1551 static void req_server (void *pParm)
1552 {
1553- unsigned priorityOfSelf = epicsThreadGetPrioritySelf ();
1554- unsigned priorityOfBeacons;
1555- epicsThreadBooleanStatus tbs;
1556- osiSockAddrNode *pNode;
1557- struct sockaddr_in serverAddr; /* server's address */
1558- osiSocklen_t addrSize = (osiSocklen_t) sizeof(struct sockaddr_in);
1559- int status;
1560- SOCKET clientSock;
1561- epicsThreadId tid;
1562- int portChange;
1563-
1564- epicsSignalInstallSigPipeIgnore ();
1565+ rsrv_iface_config *conf = pParm;
1566+ SOCKET IOC_sock;
1567
1568 taskwdInsert ( epicsThreadGetIdSelf (), NULL, NULL );
1569
1570- rsrvCurrentClient = epicsThreadPrivateCreate ();
1571-
1572- if ( envGetConfigParamPtr ( &EPICS_CAS_SERVER_PORT ) ) {
1573- ca_server_port = envGetInetPortConfigParam ( &EPICS_CAS_SERVER_PORT,
1574- (unsigned short) CA_SERVER_PORT );
1575- }
1576- else {
1577- ca_server_port = envGetInetPortConfigParam ( &EPICS_CA_SERVER_PORT,
1578- (unsigned short) CA_SERVER_PORT );
1579- }
1580-
1581- addAddrToChannelAccessAddressList ( &casIntfAddrList,
1582- &EPICS_CAS_INTF_ADDR_LIST, ca_server_port, 0 );
1583- if (ellCount(&casIntfAddrList) == 0) {
1584- pNode = (osiSockAddrNode *) calloc ( 1, sizeof(*pNode) );
1585- pNode->addr.ia.sin_family = AF_INET;
1586- pNode->addr.ia.sin_addr.s_addr = htonl ( INADDR_ANY );
1587- pNode->addr.ia.sin_port = htons ( ca_server_port );
1588- ellAdd ( &casIntfAddrList, &pNode->node );
1589- }
1590- else {
1591- if (ellCount ( &casIntfAddrList ) > 1)
1592- epicsPrintf ("CAS: Multiple entries in EPICS_CAS_INTF_ADDR_LIST, "
1593- "only the first will be used.\n");
1594- pNode = (osiSockAddrNode *) ellFirst ( &casIntfAddrList );
1595- }
1596-
1597- memcpy ( &serverAddr, &pNode->addr.ia, addrSize );
1598-
1599- if (IOC_sock != 0 && IOC_sock != INVALID_SOCKET) {
1600- epicsSocketDestroy ( IOC_sock );
1601- }
1602-
1603- /*
1604- * Open the socket. Use ARPA Internet address format and stream
1605- * sockets. Format described in <sys/socket.h>.
1606- */
1607- if ( ( IOC_sock = epicsSocketCreate (AF_INET, SOCK_STREAM, 0) ) == INVALID_SOCKET ) {
1608- errlogPrintf ("CAS: Socket creation error\n");
1609- epicsThreadSuspendSelf ();
1610- }
1611-
1612- epicsSocketEnableAddressReuseDuringTimeWaitState ( IOC_sock );
1613-
1614- /* get server's Internet address */
1615-
1616- status = bind(IOC_sock, (struct sockaddr *) &serverAddr, addrSize);
1617- if ( status < 0 ) {
1618- if ( SOCKERRNO == SOCK_EADDRINUSE ) {
1619- /*
1620- * enable assignment of a default port
1621- * (so the getsockname() call below will
1622- * work correctly)
1623- */
1624- serverAddr.sin_port = ntohs (0);
1625- status = bind(IOC_sock, (struct sockaddr *) &serverAddr, addrSize);
1626- }
1627- if ( status < 0 ) {
1628- char sockErrBuf[64];
1629- epicsSocketConvertErrnoToString (
1630- sockErrBuf, sizeof ( sockErrBuf ) );
1631- errlogPrintf ( "CAS: Socket bind error was \"%s\"\n",
1632- sockErrBuf );
1633- epicsThreadSuspendSelf ();
1634- }
1635- portChange = 1;
1636- }
1637- else {
1638- portChange = 0;
1639- }
1640-
1641- status = getsockname ( IOC_sock,
1642- (struct sockaddr *)&serverAddr, &addrSize);
1643- if ( status ) {
1644- char sockErrBuf[64];
1645- epicsSocketConvertErrnoToString (
1646- sockErrBuf, sizeof ( sockErrBuf ) );
1647- errlogPrintf ( "CAS: getsockname() error %s\n",
1648- sockErrBuf );
1649- epicsThreadSuspendSelf ();
1650- }
1651-
1652- ca_server_port = ntohs (serverAddr.sin_port);
1653-
1654- if ( portChange ) {
1655- errlogPrintf ( "cas warning: Configured TCP port was unavailable.\n");
1656- errlogPrintf ( "cas warning: Using dynamically assigned TCP port %hu,\n",
1657- ca_server_port );
1658- errlogPrintf ( "cas warning: but now two or more servers share the same UDP port.\n");
1659- errlogPrintf ( "cas warning: Depending on your IP kernel this server may not be\n" );
1660- errlogPrintf ( "cas warning: reachable with UDP unicast (a host's IP in EPICS_CA_ADDR_LIST)\n" );
1661- }
1662+ IOC_sock = conf->tcp;
1663
1664 /* listen and accept new connections */
1665 if ( listen ( IOC_sock, 20 ) < 0 ) {
1666@@ -177,25 +77,10 @@
1667 epicsThreadSuspendSelf ();
1668 }
1669
1670- tbs = epicsThreadHighestPriorityLevelBelow ( priorityOfSelf, &priorityOfBeacons );
1671- if ( tbs != epicsThreadBooleanStatusSuccess ) {
1672- priorityOfBeacons = priorityOfSelf;
1673- }
1674-
1675- beacon_startStopEvent = epicsEventMustCreate(epicsEventEmpty);
1676- beacon_ctl = ctlPause;
1677-
1678- tid = epicsThreadCreate ( "CAS-beacon", priorityOfBeacons,
1679- epicsThreadGetStackSize (epicsThreadStackSmall),
1680- rsrv_online_notify_task, 0 );
1681- if ( tid == 0 ) {
1682- epicsPrintf ( "CAS: unable to start beacon thread\n" );
1683- }
1684-
1685- epicsEventMustWait(beacon_startStopEvent);
1686 epicsEventSignal(castcp_startStopEvent);
1687
1688 while (TRUE) {
1689+ SOCKET clientSock;
1690 struct sockaddr sockAddr;
1691 osiSocklen_t addLen = sizeof(sockAddr);
1692
1693@@ -244,6 +129,275 @@
1694 }
1695 }
1696
1697+static
1698+int tryBind(SOCKET sock, const osiSockAddr* addr, const char *name)
1699+{
1700+ if(bind(sock, &addr->ia, sizeof(*addr))<0) {
1701+ char sockErrBuf[64];
1702+ if(errno!=SOCK_EADDRINUSE)
1703+ {
1704+ epicsSocketConvertErrnoToString (
1705+ sockErrBuf, sizeof ( sockErrBuf ) );
1706+ errlogPrintf ( "CAS: %s bind error: \"%s\"\n",
1707+ name, sockErrBuf );
1708+ epicsThreadSuspendSelf ();
1709+ }
1710+ return -1;
1711+ } else
1712+ return 0;
1713+}
1714+
1715+/* need to collect a set of TCP sockets, one for each interface,
1716+ * which are bound to the same TCP port number.
1717+ * Needed to avoid the complications and confusion of different TCP
1718+ * ports for each interface (name server and beacon sender would need
1719+ * to know this).
1720+ */
1721+static
1722+SOCKET* rsrv_grap_tcp(unsigned short *port)
1723+{
1724+ SOCKET *socks;
1725+ osiSockAddr scratch;
1726+ unsigned i;
1727+
1728+ socks = mallocMustSucceed(ellCount(&casIntfAddrList)*sizeof(*socks), "rsrv_grap_tcp");
1729+ for(i=0; i<ellCount(&casIntfAddrList); i++)
1730+ socks[i] = INVALID_SOCKET;
1731+
1732+ /* start with preferred port */
1733+ memset(&scratch, 0, sizeof(scratch));
1734+ scratch.ia.sin_family = AF_INET;
1735+ scratch.ia.sin_port = htons(*port);
1736+
1737+ while(ellCount(&casIntfAddrList)>0) {
1738+ ELLNODE *cur, *next;
1739+ unsigned ok = 1;
1740+
1741+ for(i=0; i<ellCount(&casIntfAddrList); i++) {
1742+ if(socks[i] != INVALID_SOCKET)
1743+ epicsSocketDestroy(socks[i]);
1744+ socks[i] = INVALID_SOCKET;
1745+ }
1746+
1747+ for (i=0, cur=ellFirst(&casIntfAddrList), next = cur ? ellNext(cur) : NULL;
1748+ cur;
1749+ i++, cur=next, next=next ? ellNext(next) : NULL)
1750+ {
1751+ SOCKET tcpsock;
1752+ osiSockAddr ifaceAddr = ((osiSockAddrNode *)cur)->addr;
1753+
1754+ scratch.ia.sin_addr = ifaceAddr.ia.sin_addr;
1755+
1756+ tcpsock = socks[i] = epicsSocketCreate (AF_INET, SOCK_STREAM, 0);
1757+ if(tcpsock==INVALID_SOCKET)
1758+ cantProceed("rsrv ran out of sockets during initialization");
1759+
1760+ epicsSocketEnableAddressReuseDuringTimeWaitState ( tcpsock );
1761+
1762+ if(bind(tcpsock, &scratch.sa, sizeof(scratch))==0) {
1763+ if(scratch.ia.sin_port==0) {
1764+ /* use first socket to pick a random port */
1765+ assert(i==0);
1766+ osiSocklen_t alen = sizeof(ifaceAddr);
1767+ if(getsockname(tcpsock, &ifaceAddr.sa, &alen)) {
1768+ char sockErrBuf[64];
1769+ epicsSocketConvertErrnoToString (
1770+ sockErrBuf, sizeof ( sockErrBuf ) );
1771+ errlogPrintf ( "CAS: getsockname error was \"%s\"\n",
1772+ sockErrBuf );
1773+ epicsThreadSuspendSelf ();
1774+ ok = 0;
1775+ break;
1776+ }
1777+ scratch.ia.sin_port = ifaceAddr.ia.sin_port;
1778+ assert(scratch.ia.sin_port!=0);
1779+ }
1780+ } else {
1781+ /* bind fails. React harshly to unexpected errors to avoid an infinite loop */
1782+ if(errno==SOCK_EADDRNOTAVAIL) {
1783+ /* this is not a bind()able address. */
1784+ int j;
1785+ char name[40];
1786+ ipAddrToDottedIP(&scratch.ia, name, sizeof(name));
1787+ printf("Skipping %s which is not an interface address\n", name);
1788+
1789+ for(j=0; j<=i; j++) {
1790+ epicsSocketDestroy(socks[j]);
1791+ socks[j] = INVALID_SOCKET;
1792+ }
1793+
1794+ ellDelete(&casIntfAddrList, cur);
1795+ free(cur);
1796+ ok = 0;
1797+ break;
1798+ }
1799+ /* if SOCK_EADDRINUSE then try again with a different port number.
1800+ * otherwise, fail hard
1801+ */
1802+ if(errno!=SOCK_EADDRINUSE && errno!=SOCK_EADDRNOTAVAIL) {
1803+ char name[40];
1804+ char sockErrBuf[64];
1805+ epicsSocketConvertErrnoToString (
1806+ sockErrBuf, sizeof ( sockErrBuf ) );
1807+ ipAddrToDottedIP(&scratch.ia, name, sizeof(name));
1808+ cantProceed( "CAS: Socket bind %s error was \"%s\"\n",
1809+ name, sockErrBuf );
1810+ }
1811+ ok = 0;
1812+ break;
1813+ }
1814+ }
1815+
1816+ if (ok) {
1817+ assert(scratch.ia.sin_port!=0);
1818+ *port = ntohs(scratch.ia.sin_port);
1819+
1820+ break;
1821+ } else {
1822+
1823+ for(i=0; i<ellCount(&casIntfAddrList); i++) {
1824+ /* cleanup any ports actually bound */
1825+ if(socks[i]!=INVALID_SOCKET) {
1826+ epicsSocketDestroy(socks[i]);
1827+ socks[i] = INVALID_SOCKET;
1828+ }
1829+ }
1830+
1831+ scratch.ia.sin_port=0; /* next iteration starts with a random port */
1832+ }
1833+ }
1834+
1835+ if(ellCount(&casIntfAddrList)==0)
1836+ cantProceed("RSRV has empty interface list\n");
1837+
1838+ return socks;
1839+}
1840+
1841+static
1842+void rsrv_build_addr_lists(void)
1843+{
1844+ ELLLIST beacon_list = ELLLIST_INIT;
1845+ /* expandbcast==0 - add bcast addresses corresponding to the provided interface addresses.
1846+ * expandbcast==1 - binding to wildcard. Fill beaconAddrList with all local addresses
1847+ */
1848+ int expandbcast = 0, autobeaconlist = 1;
1849+
1850+ /* the UDP ports are known at this point, but the TCP port is not */
1851+ assert(ca_beacon_port!=0);
1852+ assert(ca_udp_port!=0);
1853+
1854+ envGetBoolConfigParam(&EPICS_CAS_AUTO_BEACON_ADDR_LIST, &autobeaconlist);
1855+
1856+ ellInit ( &casIntfAddrList );
1857+ ellInit ( &beaconAddrList );
1858+ ellInit ( &casMCastAddrList );
1859+
1860+ {
1861+ ELLLIST temp = ELLLIST_INIT;
1862+ /* use the first parameter which is set. */
1863+ if(addAddrToChannelAccessAddressList ( &temp, &EPICS_CAS_INTF_ADDR_LIST, ca_udp_port, 0 ))
1864+ if(addAddrToChannelAccessAddressList ( &temp, &EPICS_CAS_BEACON_ADDR_LIST, ca_udp_port, 0 ))
1865+ addAddrToChannelAccessAddressList ( &temp, &EPICS_CA_ADDR_LIST, ca_udp_port, 1 );
1866+
1867+ removeDuplicateAddresses(&casIntfAddrList, &temp, 0);
1868+ }
1869+
1870+ {
1871+ /* check user provided list */
1872+ osiSockAddrNode *pNode, *pNext;
1873+ for(pNode = (osiSockAddrNode*)ellFirst(&casIntfAddrList),
1874+ pNext = pNode ? (osiSockAddrNode*)ellNext(&pNode->node) : NULL;
1875+ pNode;
1876+ pNode = pNext,
1877+ pNext = pNext ? (osiSockAddrNode*)ellNext(&pNext->node) : NULL)
1878+ {
1879+ epicsUInt32 top = ntohl(pNode->addr.ia.sin_addr.s_addr)>>24;
1880+
1881+ if(pNode->addr.ia.sin_family==AF_INET && pNode->addr.ia.sin_addr.s_addr==htonl(INADDR_ANY))
1882+ {
1883+ if (ellCount(&casIntfAddrList) != 1) {
1884+ fprintf(stderr, "CAS address list can not contain 0.0.0.0 and other addresses, ignoring...\n");
1885+ ellDelete(&casIntfAddrList, &pNode->node);
1886+ free(pNode);
1887+ } else {
1888+ expandbcast = 1;
1889+ }
1890+
1891+ } else if(pNode->addr.ia.sin_family==AF_INET && top>=224 && top<=239) {
1892+ /* This is a multi-cast address */
1893+ ellDelete(&casIntfAddrList, &pNode->node);
1894+ ellAdd(&casMCastAddrList, &pNode->node);
1895+ }
1896+ }
1897+ }
1898+
1899+ if (ellCount(&casIntfAddrList) == 0) {
1900+ /* default to wildcard 0.0.0.0 */
1901+ osiSockAddrNode *pNode = (osiSockAddrNode *) callocMustSucceed( 1, sizeof(*pNode), "rsrv_init" );
1902+ pNode->addr.ia.sin_family = AF_INET;
1903+ pNode->addr.ia.sin_addr.s_addr = htonl ( INADDR_ANY );
1904+ pNode->addr.ia.sin_port = 0;
1905+ ellAdd ( &casIntfAddrList, &pNode->node );
1906+ expandbcast = 1;
1907+ }
1908+
1909+ addAddrToChannelAccessAddressList ( &beaconAddrList,
1910+ &EPICS_CAS_BEACON_ADDR_LIST, ca_beacon_port, 0 );
1911+
1912+ beaconSocket = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0);
1913+ if (beaconSocket==INVALID_SOCKET)
1914+ cantProceed("socket allocation failed during address list expansion");
1915+
1916+
1917+ {
1918+ int intTrue = 1;
1919+ if (setsockopt (beaconSocket, SOL_SOCKET, SO_BROADCAST,
1920+ (char *)&intTrue, sizeof(intTrue))<0) {
1921+ cantProceed("CAS: online socket set up error\n");
1922+ }
1923+ }
1924+
1925+ if (!autobeaconlist) {
1926+ /* only user provided addresses */
1927+ } else if (expandbcast) {
1928+ /* add bcast addresses of all local interfaces */
1929+ osiSockAddr match;
1930+ memset(&match, 0, sizeof(match));
1931+ match.ia.sin_family = AF_INET;
1932+ match.ia.sin_addr.s_addr = htonl(INADDR_ANY);
1933+ match.ia.sin_port = htons(ca_beacon_port);
1934+
1935+ osiSockDiscoverBroadcastAddresses(&beacon_list, beaconSocket, &match);
1936+
1937+ } else {
1938+ /* add bcast addresses of only specified interfaces */
1939+ osiSockAddrNode *pNode;
1940+
1941+ for(pNode = (osiSockAddrNode*)ellFirst(&casIntfAddrList);
1942+ pNode;
1943+ pNode = (osiSockAddrNode*)ellNext(&pNode->node))
1944+ {
1945+ osiSockDiscoverBroadcastAddresses(&beacon_list, beaconSocket, &pNode->addr);
1946+ }
1947+ }
1948+
1949+ {
1950+ /* set the port for any automatically discovered destinations. */
1951+ osiSockAddrNode *pNode;
1952+ for(pNode = (osiSockAddrNode*)ellFirst(&beacon_list);
1953+ pNode;
1954+ pNode = (osiSockAddrNode*)ellNext(&pNode->node))
1955+ {
1956+ pNode->addr.ia.sin_port = htons(ca_beacon_port);
1957+ }
1958+ }
1959+
1960+ ellConcat(&beaconAddrList, &beacon_list);
1961+
1962+ if (ellCount(&beaconAddrList)==0)
1963+ fprintf(stderr, "Warning: RSRV has empty beacon address list\n");
1964+}
1965+
1966 static dbServer rsrv_server = {
1967 ELLNODE_INIT,
1968 "rsrv",
1969@@ -257,11 +411,9 @@
1970 */
1971 int rsrv_init (void)
1972 {
1973- epicsThreadBooleanStatus tbs;
1974- unsigned priorityOfConnectDaemon;
1975- epicsThreadId tid;
1976 long maxBytesAsALong;
1977 long status;
1978+ SOCKET *socks;
1979
1980 clientQlock = epicsMutexMustCreate();
1981
1982@@ -272,8 +424,31 @@
1983 freeListInitPvt ( &rsrvSmallBufFreeListTCP, MAX_TCP, 16 );
1984 initializePutNotifyFreeList ();
1985
1986+ epicsSignalInstallSigPipeIgnore ();
1987+
1988+ rsrvCurrentClient = epicsThreadPrivateCreate ();
1989+
1990 dbRegisterServer(&rsrv_server);
1991
1992+ if ( envGetConfigParamPtr ( &EPICS_CAS_SERVER_PORT ) ) {
1993+ ca_server_port = envGetInetPortConfigParam ( &EPICS_CAS_SERVER_PORT,
1994+ (unsigned short) CA_SERVER_PORT );
1995+ }
1996+ else {
1997+ ca_server_port = envGetInetPortConfigParam ( &EPICS_CA_SERVER_PORT,
1998+ (unsigned short) CA_SERVER_PORT );
1999+ }
2000+ ca_udp_port = ca_server_port;
2001+
2002+ if (envGetConfigParamPtr(&EPICS_CAS_BEACON_PORT)) {
2003+ ca_beacon_port = envGetInetPortConfigParam (&EPICS_CAS_BEACON_PORT,
2004+ (unsigned short) CA_REPEATER_PORT );
2005+ }
2006+ else {
2007+ ca_beacon_port = envGetInetPortConfigParam (&EPICS_CA_REPEATER_PORT,
2008+ (unsigned short) CA_REPEATER_PORT );
2009+ }
2010+
2011 status = envGetLongConfigParam ( &EPICS_CA_MAX_ARRAY_BYTES, &maxBytesAsALong );
2012 if ( status || maxBytesAsALong < 0 ) {
2013 errlogPrintf ( "CAS: EPICS_CA_MAX_ARRAY_BYTES was not a positive integer\n" );
2014@@ -298,41 +473,223 @@
2015 }
2016 }
2017 freeListInitPvt ( &rsrvLargeBufFreeListTCP, rsrvSizeofLargeBufTCP, 1 );
2018- ellInit ( &casIntfAddrList );
2019- ellInit ( &beaconAddrList );
2020- prsrv_cast_client = NULL;
2021- pCaBucket = NULL;
2022+ pCaBucket = bucketCreate(CAS_HASH_TABLE_SIZE);
2023+ if (!pCaBucket)
2024+ cantProceed("RSRV failed to allocate ID lookup table\n");
2025+
2026+ rsrv_build_addr_lists();
2027
2028 castcp_startStopEvent = epicsEventMustCreate(epicsEventEmpty);
2029+ casudp_startStopEvent = epicsEventMustCreate(epicsEventEmpty);
2030+ beacon_startStopEvent = epicsEventMustCreate(epicsEventEmpty);
2031 castcp_ctl = ctlPause;
2032
2033- /*
2034- * go down two levels so that we are below
2035- * the TCP and event threads started on behalf
2036- * of individual clients
2037- */
2038- tbs = epicsThreadHighestPriorityLevelBelow (
2039- epicsThreadPriorityCAServerLow, &priorityOfConnectDaemon );
2040- if ( tbs == epicsThreadBooleanStatusSuccess ) {
2041- tbs = epicsThreadHighestPriorityLevelBelow (
2042- priorityOfConnectDaemon, &priorityOfConnectDaemon );
2043- if ( tbs != epicsThreadBooleanStatusSuccess ) {
2044- priorityOfConnectDaemon = epicsThreadPriorityCAServerLow;
2045- }
2046- }
2047- else {
2048- priorityOfConnectDaemon = epicsThreadPriorityCAServerLow;
2049- }
2050-
2051- tid = epicsThreadCreate ( "CAS-TCP",
2052- priorityOfConnectDaemon,
2053- epicsThreadGetStackSize(epicsThreadStackMedium),
2054- req_server, 0);
2055- if ( tid == 0 ) {
2056- epicsPrintf ( "CAS: unable to start connection request thread\n" );
2057- }
2058-
2059- epicsEventMustWait(castcp_startStopEvent);
2060+ /* Thread priorites
2061+ * Now starting per interface
2062+ * TCP Listener: epicsThreadPriorityCAServerLow-2
2063+ * Name receiver: epicsThreadPriorityCAServerLow-4
2064+ * Now starting global
2065+ * Beacon sender: epicsThreadPriorityCAServerLow-3
2066+ * Started later per TCP client
2067+ * TCP receiver: epicsThreadPriorityCAServerLow
2068+ * TCP sender : epicsThreadPriorityCAServerLow-1
2069+ */
2070+ {
2071+ unsigned i;
2072+ threadPrios[0] = epicsThreadPriorityCAServerLow;
2073+
2074+ for(i=1; i<NELEMENTS(threadPrios); i++)
2075+ {
2076+ if(epicsThreadBooleanStatusSuccess!=epicsThreadHighestPriorityLevelBelow(
2077+ threadPrios[i-1], &threadPrios[i]))
2078+ {
2079+ /* on failure use the lowest known */
2080+ threadPrios[i] = threadPrios[i-1];
2081+ }
2082+ }
2083+ }
2084+
2085+ {
2086+ unsigned short sport = ca_server_port;
2087+ socks = rsrv_grap_tcp(&sport);
2088+
2089+ if ( sport != ca_server_port ) {
2090+ ca_server_port = sport;
2091+ errlogPrintf ( "cas warning: Configured TCP port was unavailable.\n");
2092+ errlogPrintf ( "cas warning: Using dynamically assigned TCP port %hu,\n",
2093+ ca_server_port );
2094+ errlogPrintf ( "cas warning: but now two or more servers share the same UDP port.\n");
2095+ errlogPrintf ( "cas warning: Depending on your IP kernel this server may not be\n" );
2096+ errlogPrintf ( "cas warning: reachable with UDP unicast (a host's IP in EPICS_CA_ADDR_LIST)\n" );
2097+ }
2098+ }
2099+
2100+ /* start servers (TCP and UDP(s) for each interface.
2101+ */
2102+ {
2103+ int havesometcp = 0;
2104+ ELLNODE *cur;
2105+ int i;
2106+
2107+ for (i=0, cur=ellFirst(&casIntfAddrList); cur; i++, cur=ellNext(cur))
2108+ {
2109+ char ifaceName[40];
2110+ rsrv_iface_config *conf;
2111+
2112+ conf = callocMustSucceed(1, sizeof(*conf), "rsrv_init");
2113+
2114+ conf->tcpAddr = ((osiSockAddrNode *)cur)->addr;
2115+ conf->tcpAddr.ia.sin_port = htons(ca_server_port);
2116+ conf->tcp = socks[i];
2117+ socks[i] = INVALID_SOCKET;
2118+
2119+ ipAddrToDottedIP (&conf->tcpAddr.ia, ifaceName, sizeof(ifaceName));
2120+
2121+ conf->udp = conf->udpbcast = INVALID_SOCKET;
2122+
2123+ /* create and bind UDP name receiver socket(s) */
2124+
2125+ conf->udp = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0);
2126+ if(conf->udp==INVALID_SOCKET)
2127+ cantProceed("rsrv_init ran out of udp sockets");
2128+
2129+ conf->udpAddr = conf->tcpAddr;
2130+ conf->udpAddr.ia.sin_port = htons(ca_udp_port);
2131+
2132+ epicsSocketEnableAddressUseForDatagramFanout ( conf->udp );
2133+
2134+ if(tryBind(conf->udp, &conf->udpAddr, "UDP unicast socket"))
2135+ goto cleanup;
2136+
2137+#ifdef IP_ADD_MEMBERSHIP
2138+ /* join UDP socket to any multicast groups */
2139+ {
2140+ osiSockAddrNode *pNode;
2141+
2142+ for(pNode = (osiSockAddrNode*)ellFirst(&casMCastAddrList);
2143+ pNode;
2144+ pNode = (osiSockAddrNode*)ellNext(&pNode->node))
2145+ {
2146+ struct ip_mreq mreq;
2147+
2148+ memset(&mreq, 0, sizeof(mreq));
2149+ mreq.imr_multiaddr = pNode->addr.ia.sin_addr;
2150+ mreq.imr_interface.s_addr = conf->udpAddr.ia.sin_addr.s_addr;
2151+
2152+ if(setsockopt(conf->udp, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq))!=0)
2153+ {
2154+ struct sockaddr_in temp;
2155+ char name[40];
2156+ char sockErrBuf[64];
2157+ temp.sin_family = AF_INET;
2158+ temp.sin_addr = mreq.imr_multiaddr;
2159+ temp.sin_port = conf->udpAddr.ia.sin_port;
2160+ epicsSocketConvertErrnoToString (
2161+ sockErrBuf, sizeof ( sockErrBuf ) );
2162+ ipAddrToDottedIP (&temp, name, sizeof(name));
2163+ fprintf(stderr, "CAS: Socket mcast join %s to %s failed with \"%s\"\n",
2164+ ifaceName, name, sockErrBuf );
2165+ }
2166+ }
2167+ }
2168+#else
2169+ if(ellCount(&casMCastAddrList)){
2170+ fprintf(stderr, "IPv4 Multicast name lookup not supported by this target\n");
2171+ }
2172+#endif
2173+
2174+#if !defined(_WIN32)
2175+ /* An oddness of BSD sockets (not winsock) is that binding to
2176+ * INADDR_ANY will receive unicast and broadcast, but binding to
2177+ * a specific interface address receives only unicast. The trick
2178+ * is to bind a second socket to the interface broadcast address,
2179+ * which will then receive only broadcasts.
2180+ */
2181+ if(conf->udpAddr.ia.sin_addr.s_addr!=htonl(INADDR_ANY)) {
2182+ /* find interface broadcast address */
2183+ ELLLIST bcastList = ELLLIST_INIT;
2184+ osiSockAddrNode *pNode;
2185+
2186+ osiSockDiscoverBroadcastAddresses (&bcastList,
2187+ conf->udp, &conf->udpAddr); // match addr
2188+
2189+ if(ellCount(&bcastList)==0) {
2190+ fprintf(stderr, "Warning: Can't find broadcast address of interface %s\n"
2191+ " Name lookup may not work on this interface\n", ifaceName);
2192+ } else {
2193+ if(ellCount(&bcastList)>1 && conf->udpAddr.ia.sin_addr.s_addr!=htonl(INADDR_ANY))
2194+ printf("Interface %s has more than one broadcast address?\n", ifaceName);
2195+
2196+ pNode = (osiSockAddrNode*)ellFirst(&bcastList);
2197+
2198+ conf->udpbcast = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0);
2199+ if(conf->udpbcast==INVALID_SOCKET)
2200+ cantProceed("rsrv_init ran out of udp sockets for bcast");
2201+
2202+ epicsSocketEnableAddressUseForDatagramFanout ( conf->udpbcast );
2203+
2204+ conf->udpbcastAddr = conf->udpAddr;
2205+ conf->udpbcastAddr.ia.sin_addr.s_addr = pNode->addr.ia.sin_addr.s_addr;
2206+
2207+ if(tryBind(conf->udpbcast, &conf->udpbcastAddr, "UDP Socket bcast"))
2208+ goto cleanup;
2209+ }
2210+
2211+ ellFree(&bcastList);
2212+ }
2213+
2214+ ellAdd(&servers, &conf->node);
2215+
2216+#endif /* !defined(_WIN32) */
2217+
2218+ /* have all sockets, time to start some threads */
2219+
2220+ epicsThreadMustCreate("CAS-TCP", threadPrios[2],
2221+ epicsThreadGetStackSize(epicsThreadStackMedium),
2222+ &req_server, conf);
2223+
2224+ epicsEventMustWait(castcp_startStopEvent);
2225+
2226+ epicsThreadMustCreate("CAS-UDP", threadPrios[4],
2227+ epicsThreadGetStackSize(epicsThreadStackMedium),
2228+ &cast_server, conf);
2229+
2230+ epicsEventMustWait(casudp_startStopEvent);
2231+
2232+#if !defined(_WIN32)
2233+ if(conf->udpbcast != INVALID_SOCKET) {
2234+ conf->startbcast = 1;
2235+
2236+ epicsThreadMustCreate("CAS-UDP2", threadPrios[4],
2237+ epicsThreadGetStackSize(epicsThreadStackMedium),
2238+ &cast_server, conf);
2239+
2240+ epicsEventMustWait(casudp_startStopEvent);
2241+
2242+ conf->startbcast = 0;
2243+ }
2244+#endif /* !defined(_WIN32) */
2245+
2246+ havesometcp = 1;
2247+ continue;
2248+ cleanup:
2249+ epicsSocketDestroy(conf->tcp);
2250+ if(conf->udp!=INVALID_SOCKET) epicsSocketDestroy(conf->udp);
2251+ if(conf->udpbcast!=INVALID_SOCKET) epicsSocketDestroy(conf->udpbcast);
2252+ free(conf);
2253+ }
2254+
2255+ if(!havesometcp)
2256+ cantProceed("CAS: No TCP server started\n");
2257+ }
2258+
2259+ /* servers list is considered read-only from this point */
2260+
2261+ epicsThreadMustCreate("CAS-beacon", threadPrios[3],
2262+ epicsThreadGetStackSize(epicsThreadStackSmall),
2263+ &rsrv_online_notify_task, NULL);
2264+
2265+ epicsEventMustWait(beacon_startStopEvent);
2266
2267 return RSRV_OK;
2268 }
2269@@ -499,11 +856,49 @@
2270 log_one_client(client, level);
2271 client = (struct client *) ellNext(&client->node);
2272 }
2273+
2274+ if (level>=2) {
2275+ rsrv_iface_config *client = (rsrv_iface_config *) ellFirst ( &servers );
2276+ while (client) {
2277+ char buf[40];
2278+
2279+ ipAddrToDottedIP (&client->tcpAddr.ia, buf, sizeof(buf));
2280+ printf("Server interface %s\n", buf);
2281+
2282+ ipAddrToDottedIP (&client->udpAddr.ia, buf, sizeof(buf));
2283+ printf(" UDP receiver 1 %s\n", buf);
2284+
2285+#if !defined(_WIN32)
2286+ if(client->udpbcast!=INVALID_SOCKET) {
2287+ ipAddrToDottedIP (&client->udpbcastAddr.ia, buf, sizeof(buf));
2288+ printf(" UDP receiver 2 %s\n", buf);
2289+ }
2290+#endif
2291+
2292+ client = (rsrv_iface_config *) ellNext(&client->node);
2293+ }
2294+ }
2295 UNLOCK_CLIENTQ
2296-
2297- if (level>=2 && prsrv_cast_client) {
2298- printf( "UDP Server:\n" );
2299- log_one_client(prsrv_cast_client, level);
2300+ if (level>=2) {
2301+ osiSockAddrNode * pAddr;
2302+ for(pAddr = (osiSockAddrNode*)ellFirst(&casMCastAddrList);
2303+ pAddr;
2304+ pAddr = (osiSockAddrNode*)ellNext(&pAddr->node))
2305+ {
2306+ char buf[40];
2307+
2308+ ipAddrToDottedIP (&pAddr->addr.ia, buf, sizeof(buf));
2309+ printf("MCast destination %s\n", buf);
2310+ }
2311+ for(pAddr = (osiSockAddrNode*)ellFirst(&beaconAddrList);
2312+ pAddr;
2313+ pAddr = (osiSockAddrNode*)ellNext(&pAddr->node))
2314+ {
2315+ char buf[40];
2316+
2317+ ipAddrToDottedIP (&pAddr->addr.ia, buf, sizeof(buf));
2318+ printf("Beacon destination %s\n", buf);
2319+ }
2320 }
2321
2322 if (level>=2u) {
2323@@ -532,12 +927,10 @@
2324 MAX_TCP,
2325 (unsigned int) freeListItemsAvail ( rsrvLargeBufFreeListTCP ),
2326 rsrvSizeofLargeBufTCP );
2327- if(pCaBucket){
2328- printf( "The server's resource id conversion table:\n");
2329- LOCK_CLIENTQ;
2330- bucketShow (pCaBucket);
2331- UNLOCK_CLIENTQ;
2332- }
2333+ printf( "The server's resource id conversion table:\n");
2334+ LOCK_CLIENTQ;
2335+ bucketShow (pCaBucket);
2336+ UNLOCK_CLIENTQ;
2337 printf ( "The server's array size limit is %u bytes max\n",
2338 rsrvSizeofLargeBufTCP );
2339
2340
2341=== modified file 'src/ioc/rsrv/cast_server.c'
2342--- src/ioc/rsrv/cast_server.c 2014-10-06 05:57:02 +0000
2343+++ src/ioc/rsrv/cast_server.c 2016-01-12 02:07:47 +0000
2344@@ -54,7 +54,7 @@
2345 /*
2346 * clean_addrq
2347 */
2348-static void clean_addrq(void)
2349+static void clean_addrq(struct client *client)
2350 {
2351 struct channel_in_use * pciu;
2352 struct channel_in_use * pnextciu;
2353@@ -67,9 +67,9 @@
2354
2355 epicsTimeGetCurrent ( &current );
2356
2357- epicsMutexMustLock ( prsrv_cast_client->chanListLock );
2358+ epicsMutexMustLock ( client->chanListLock );
2359 pnextciu = (struct channel_in_use *)
2360- prsrv_cast_client->chanList.node.next;
2361+ client->chanList.node.next;
2362
2363 while( (pciu = pnextciu) ) {
2364 pnextciu = (struct channel_in_use *)pciu->node.next;
2365@@ -77,7 +77,7 @@
2366 delay = epicsTimeDiffInSeconds(&current,&pciu->time_at_creation);
2367 if (delay > timeout) {
2368
2369- ellDelete(&prsrv_cast_client->chanList, &pciu->node);
2370+ ellDelete(&client->chanList, &pciu->node);
2371 LOCK_CLIENTQ;
2372 s = bucketRemoveItemUnsignedId (
2373 pCaBucket,
2374@@ -96,7 +96,7 @@
2375 if(delay>maxdelay) maxdelay = delay;
2376 }
2377 }
2378- epicsMutexUnlock ( prsrv_cast_client->chanListLock );
2379+ epicsMutexUnlock ( client->chanListLock );
2380
2381 # ifdef DEBUG
2382 if(ndelete){
2383@@ -115,70 +115,23 @@
2384 */
2385 void cast_server(void *pParm)
2386 {
2387- osiSockAddrNode *paddrNode;
2388- struct sockaddr_in sin;
2389+ rsrv_iface_config *conf = pParm;
2390 int status;
2391 int count=0;
2392+ int mysocket=0;
2393 struct sockaddr_in new_recv_addr;
2394 osiSocklen_t recv_addr_size;
2395 osiSockIoctl_t nchars;
2396+ SOCKET recv_sock, reply_sock;
2397+ struct client *client;
2398
2399 recv_addr_size = sizeof(new_recv_addr);
2400
2401- if( IOC_cast_sock!=0 && IOC_cast_sock!=INVALID_SOCKET ) {
2402- epicsSocketDestroy ( IOC_cast_sock );
2403- }
2404-
2405- /*
2406- * Open the socket.
2407- * Use ARPA Internet address format and datagram socket.
2408- */
2409-
2410- if ( ( IOC_cast_sock = epicsSocketCreate (AF_INET, SOCK_DGRAM, 0) ) == INVALID_SOCKET ) {
2411- epicsPrintf ("CAS: cast socket creation error\n");
2412- epicsThreadSuspendSelf ();
2413- }
2414-
2415- /*
2416- * some concern that vxWorks will run out of mBuf's
2417- * if this change is made
2418- *
2419- * joh 11-10-98
2420- */
2421-#if 0
2422- {
2423- /*
2424- *
2425- * this allows for faster connects by queuing
2426- * additional incomming UDP search frames
2427- *
2428- * this allocates a 32k buffer
2429- * (uses a power of two)
2430- */
2431- int size = 1u<<15u;
2432- status = setsockopt (IOC_cast_sock, SOL_SOCKET,
2433- SO_RCVBUF, (char *)&size, sizeof(size));
2434- if (status<0) {
2435- epicsPrintf ("CAS: unable to set cast socket size\n");
2436- }
2437- }
2438-#endif
2439-
2440- epicsSocketEnableAddressUseForDatagramFanout ( IOC_cast_sock );
2441-
2442- paddrNode = (osiSockAddrNode *) ellFirst ( &casIntfAddrList );
2443-
2444- memcpy(&sin, &paddrNode->addr.ia, sizeof (sin));
2445-
2446- /* get server's Internet address */
2447- if( bind(IOC_cast_sock, (struct sockaddr *)&sin, sizeof (sin)) < 0){
2448- char sockErrBuf[64];
2449- epicsSocketConvertErrnoToString (
2450- sockErrBuf, sizeof ( sockErrBuf ) );
2451- epicsPrintf ("CAS: UDP server port bind error was \"%s\"\n", sockErrBuf );
2452- epicsSocketDestroy ( IOC_cast_sock );
2453- epicsThreadSuspendSelf ();
2454- }
2455+ reply_sock = conf->udp;
2456+ if(conf->startbcast)
2457+ recv_sock = conf->udpbcast;
2458+ else
2459+ recv_sock = conf->udp;
2460
2461 /*
2462 * setup new client structure but reuse old structure if
2463@@ -186,27 +139,31 @@
2464 *
2465 */
2466 while ( TRUE ) {
2467- prsrv_cast_client = create_client ( IOC_cast_sock, IPPROTO_UDP );
2468- if ( prsrv_cast_client ) {
2469+ client = create_client ( reply_sock, IPPROTO_UDP );
2470+ if ( client ) {
2471 break;
2472 }
2473 epicsThreadSleep(300.0);
2474 }
2475+ client->udpRecv = recv_sock;
2476
2477- casAttachThreadToClient ( prsrv_cast_client );
2478+ casAttachThreadToClient ( client );
2479
2480 /*
2481 * add placeholder for the first version message should it be needed
2482 */
2483- rsrv_version_reply ( prsrv_cast_client );
2484+ rsrv_version_reply ( client );
2485+
2486+ /* these pointers become invalid after signaling casudp_startStopEvent */
2487+ conf = NULL;
2488
2489 epicsEventSignal(casudp_startStopEvent);
2490
2491 while (TRUE) {
2492 status = recvfrom (
2493- IOC_cast_sock,
2494- prsrv_cast_client->recv.buf,
2495- prsrv_cast_client->recv.maxstk,
2496+ recv_sock,
2497+ client->recv.buf,
2498+ client->recv.maxstk,
2499 0,
2500 (struct sockaddr *)&new_recv_addr,
2501 &recv_addr_size);
2502@@ -221,69 +178,69 @@
2503 }
2504 }
2505 else if (casudp_ctl == ctlRun) {
2506- prsrv_cast_client->recv.cnt = (unsigned) status;
2507- prsrv_cast_client->recv.stk = 0ul;
2508- epicsTimeGetCurrent(&prsrv_cast_client->time_at_last_recv);
2509+ client->recv.cnt = (unsigned) status;
2510+ client->recv.stk = 0ul;
2511+ epicsTimeGetCurrent(&client->time_at_last_recv);
2512
2513- prsrv_cast_client->minor_version_number = 0;
2514- prsrv_cast_client->seqNoOfReq = 0;
2515+ client->minor_version_number = 0;
2516+ client->seqNoOfReq = 0;
2517
2518 /*
2519 * If we are talking to a new client flush to the old one
2520 * in case we are holding UDP messages waiting to
2521 * see if the next message is for this same client.
2522 */
2523- if (prsrv_cast_client->send.stk>sizeof(caHdr)) {
2524- status = memcmp(&prsrv_cast_client->addr,
2525+ if (client->send.stk>sizeof(caHdr)) {
2526+ status = memcmp(&client->addr,
2527 &new_recv_addr, recv_addr_size);
2528 if(status){
2529 /*
2530 * if the address is different
2531 */
2532- cas_send_dg_msg(prsrv_cast_client);
2533- prsrv_cast_client->addr = new_recv_addr;
2534+ cas_send_dg_msg(client);
2535+ client->addr = new_recv_addr;
2536 }
2537 }
2538 else {
2539- prsrv_cast_client->addr = new_recv_addr;
2540+ client->addr = new_recv_addr;
2541 }
2542
2543 if (CASDEBUG>1) {
2544 char buf[40];
2545
2546- ipAddrToDottedIP (&prsrv_cast_client->addr, buf, sizeof(buf));
2547+ ipAddrToDottedIP (&client->addr, buf, sizeof(buf));
2548 errlogPrintf ("CAS: cast server msg of %d bytes from addr %s\n",
2549- prsrv_cast_client->recv.cnt, buf);
2550+ client->recv.cnt, buf);
2551 }
2552
2553 if (CASDEBUG>2)
2554- count = ellCount (&prsrv_cast_client->chanList);
2555+ count = ellCount (&client->chanList);
2556
2557- status = camessage ( prsrv_cast_client );
2558+ status = camessage ( client );
2559 if(status == RSRV_OK){
2560- if(prsrv_cast_client->recv.cnt !=
2561- prsrv_cast_client->recv.stk){
2562+ if(client->recv.cnt !=
2563+ client->recv.stk){
2564 char buf[40];
2565
2566- ipAddrToDottedIP (&prsrv_cast_client->addr, buf, sizeof(buf));
2567+ ipAddrToDottedIP (&client->addr, buf, sizeof(buf));
2568
2569 epicsPrintf ("CAS: partial (damaged?) UDP msg of %d bytes from %s ?\n",
2570- prsrv_cast_client->recv.cnt-prsrv_cast_client->recv.stk, buf);
2571+ client->recv.cnt-client->recv.stk, buf);
2572 }
2573 }
2574 else {
2575 char buf[40];
2576
2577- ipAddrToDottedIP (&prsrv_cast_client->addr, buf, sizeof(buf));
2578+ ipAddrToDottedIP (&client->addr, buf, sizeof(buf));
2579
2580 epicsPrintf ("CAS: invalid (damaged?) UDP request from %s ?\n", buf);
2581 }
2582
2583 if (CASDEBUG>2) {
2584- if ( ellCount (&prsrv_cast_client->chanList) ) {
2585+ if ( ellCount (&client->chanList) ) {
2586 errlogPrintf ("CAS: Fnd %d name matches (%d tot)\n",
2587- ellCount(&prsrv_cast_client->chanList)-count,
2588- ellCount(&prsrv_cast_client->chanList));
2589+ ellCount(&client->chanList)-count,
2590+ ellCount(&client->chanList));
2591 }
2592 }
2593 }
2594@@ -292,15 +249,22 @@
2595 * allow messages to batch up if more are comming
2596 */
2597 nchars = 0; /* supress purify warning */
2598- status = socket_ioctl(IOC_cast_sock, FIONREAD, &nchars);
2599+ status = socket_ioctl(recv_sock, FIONREAD, &nchars);
2600 if (status<0) {
2601 errlogPrintf ("CA cast server: Unable to fetch N characters pending\n");
2602- cas_send_dg_msg (prsrv_cast_client);
2603- clean_addrq ();
2604+ cas_send_dg_msg (client);
2605+ clean_addrq (client);
2606 }
2607 else if (nchars == 0) {
2608- cas_send_dg_msg (prsrv_cast_client);
2609- clean_addrq ();
2610+ cas_send_dg_msg (client);
2611+ clean_addrq (client);
2612 }
2613 }
2614+
2615+ /* ATM never reached, just a placeholder */
2616+
2617+ if(!mysocket)
2618+ client->sock = INVALID_SOCKET; /* only one cast_server should destroy the reply socket */
2619+ destroy_client(client);
2620+ epicsSocketDestroy(recv_sock);
2621 }
2622
2623=== modified file 'src/ioc/rsrv/online_notify.c'
2624--- src/ioc/rsrv/online_notify.c 2014-10-06 05:57:02 +0000
2625+++ src/ioc/rsrv/online_notify.c 2016-01-12 02:07:47 +0000
2626@@ -36,45 +36,18 @@
2627 #include "server.h"
2628
2629 /*
2630- * forcePort ()
2631- */
2632-static void forcePort (ELLLIST *pList, unsigned short port)
2633-{
2634- osiSockAddrNode *pNode;
2635-
2636- pNode = (osiSockAddrNode *) ellFirst ( pList );
2637- while ( pNode ) {
2638- if ( pNode->addr.sa.sa_family == AF_INET ) {
2639- pNode->addr.ia.sin_port = htons ( port );
2640- }
2641- pNode = (osiSockAddrNode *) ellNext ( &pNode->node );
2642- }
2643-}
2644-
2645-/*
2646 * RSRV_ONLINE_NOTIFY_TASK
2647 */
2648 void rsrv_online_notify_task(void *pParm)
2649 {
2650- unsigned priorityOfSelf = epicsThreadGetPrioritySelf ();
2651- osiSockAddrNode *pNode;
2652 double delay;
2653 double maxdelay;
2654 long longStatus;
2655 double maxPeriod;
2656 caHdr msg;
2657 int status;
2658- SOCKET sock;
2659- int intTrue = TRUE;
2660- unsigned short port;
2661 ca_uint32_t beaconCounter = 0;
2662- char * pStr;
2663- int autoBeaconAddr;
2664- ELLLIST autoAddrList;
2665 char buf[16];
2666- unsigned priorityOfUDP;
2667- epicsThreadBooleanStatus tbs;
2668- epicsThreadId tid;
2669
2670 taskwdInsert (epicsThreadGetIdSelf(),NULL,NULL);
2671
2672@@ -94,189 +67,34 @@
2673
2674 delay = 0.02; /* initial beacon period in sec */
2675 maxdelay = maxPeriod;
2676-
2677- /*
2678- * Open the socket.
2679- * Use ARPA Internet address format and datagram socket.
2680- * Format described in <sys/socket.h>.
2681- */
2682- if ( (sock = epicsSocketCreate (AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) {
2683- errlogPrintf ("CAS: online socket creation error\n");
2684- epicsThreadSuspendSelf ();
2685- }
2686-
2687- status = setsockopt (sock, SOL_SOCKET, SO_BROADCAST,
2688- (char *)&intTrue, sizeof(intTrue));
2689- if (status<0) {
2690- errlogPrintf ("CAS: online socket set up error\n");
2691- epicsThreadSuspendSelf ();
2692- }
2693
2694- {
2695- /*
2696- * this connect is to supress a warning message on Linux
2697- * when we shutdown the read side of the socket. If it
2698- * fails (and it will on old ip kernels) we just ignore
2699- * the failure.
2700- */
2701- osiSockAddr sockAddr;
2702- sockAddr.ia.sin_family = AF_UNSPEC;
2703- sockAddr.ia.sin_port = htons ( 0 );
2704- sockAddr.ia.sin_addr.s_addr = htonl (0);
2705- connect ( sock, & sockAddr.sa, sizeof ( sockAddr.sa ) );
2706- shutdown ( sock, SHUT_RD );
2707- }
2708-
2709 memset((char *)&msg, 0, sizeof msg);
2710 msg.m_cmmd = htons (CA_PROTO_RSRV_IS_UP);
2711 msg.m_count = htons (ca_server_port);
2712 msg.m_dataType = htons (CA_MINOR_PROTOCOL_REVISION);
2713-
2714- ellInit ( & beaconAddrList );
2715- ellInit ( & autoAddrList );
2716-
2717- pStr = envGetConfigParam(&EPICS_CAS_AUTO_BEACON_ADDR_LIST, sizeof(buf), buf);
2718- if ( ! pStr ) {
2719- pStr = envGetConfigParam(&EPICS_CA_AUTO_ADDR_LIST, sizeof(buf), buf);
2720- }
2721- if (pStr) {
2722- if (strstr(pStr,"no")||strstr(pStr,"NO")) {
2723- autoBeaconAddr = FALSE;
2724- }
2725- else if (strstr(pStr,"yes")||strstr(pStr,"YES")) {
2726- autoBeaconAddr = TRUE;
2727- }
2728- else {
2729- fprintf(stderr,
2730- "CAS: EPICS_CA(S)_AUTO_ADDR_LIST = \"%s\"? Assuming \"YES\"\n", pStr);
2731- autoBeaconAddr = TRUE;
2732- }
2733- }
2734- else {
2735- autoBeaconAddr = TRUE;
2736- }
2737-
2738- /*
2739- * load user and auto configured
2740- * broadcast address list
2741- */
2742- if (envGetConfigParamPtr(&EPICS_CAS_BEACON_PORT)) {
2743- port = envGetInetPortConfigParam (&EPICS_CAS_BEACON_PORT,
2744- (unsigned short) CA_REPEATER_PORT );
2745- }
2746- else {
2747- port = envGetInetPortConfigParam (&EPICS_CA_REPEATER_PORT,
2748- (unsigned short) CA_REPEATER_PORT );
2749- }
2750-
2751- /*
2752- * discover beacon addresses associated with this interface
2753- */
2754- if ( autoBeaconAddr ) {
2755- osiSockAddr addr;
2756- ELLLIST tmpList;
2757-
2758- ellInit ( &tmpList );
2759- addr.ia.sin_family = AF_UNSPEC;
2760- osiSockDiscoverBroadcastAddresses (&tmpList, sock, &addr);
2761- forcePort ( &tmpList, port );
2762- removeDuplicateAddresses ( &autoAddrList, &tmpList, 1 );
2763- }
2764-
2765- /*
2766- * by default use EPICS_CA_ADDR_LIST for the
2767- * beacon address list
2768- */
2769- {
2770- const ENV_PARAM *pParam;
2771-
2772- if (envGetConfigParamPtr(&EPICS_CAS_INTF_ADDR_LIST) ||
2773- envGetConfigParamPtr(&EPICS_CAS_BEACON_ADDR_LIST)) {
2774- pParam = &EPICS_CAS_BEACON_ADDR_LIST;
2775- }
2776- else {
2777- pParam = &EPICS_CA_ADDR_LIST;
2778- }
2779-
2780- /*
2781- * add in the configured addresses
2782- */
2783- addAddrToChannelAccessAddressList (
2784- &autoAddrList, pParam, port, pParam == &EPICS_CA_ADDR_LIST );
2785- }
2786-
2787- removeDuplicateAddresses ( &beaconAddrList, &autoAddrList, 0 );
2788-
2789- if ( ellCount ( &beaconAddrList ) == 0 ) {
2790- errlogPrintf ("The CA server's beacon address list was empty after initialization?\n");
2791- }
2792-
2793-# ifdef DEBUG
2794- printChannelAccessAddressList (&beaconAddrList);
2795-# endif
2796-
2797- tbs = epicsThreadHighestPriorityLevelBelow ( priorityOfSelf, &priorityOfUDP );
2798- if ( tbs != epicsThreadBooleanStatusSuccess ) {
2799- priorityOfUDP = priorityOfSelf;
2800- }
2801-
2802- casudp_startStopEvent = epicsEventMustCreate(epicsEventEmpty);
2803- casudp_ctl = ctlPause;
2804-
2805- tid = epicsThreadCreate ( "CAS-UDP", priorityOfUDP,
2806- epicsThreadGetStackSize (epicsThreadStackMedium),
2807- cast_server, 0 );
2808- if ( tid == 0 ) {
2809- epicsPrintf ( "CAS: unable to start UDP daemon thread\n" );
2810- }
2811-
2812- epicsEventMustWait(casudp_startStopEvent);
2813+
2814+
2815 epicsEventSignal(beacon_startStopEvent);
2816
2817 while (TRUE) {
2818- pNode = (osiSockAddrNode *) ellFirst (&beaconAddrList);
2819- while (pNode) {
2820- char buf[64];
2821+ ELLNODE *cur;
2822
2823- status = connect (sock, &pNode->addr.sa,
2824- sizeof(pNode->addr.sa));
2825- if (status<0) {
2826+ /* send beacon to each interface */
2827+ for(cur=ellFirst(&beaconAddrList); cur; cur=ellNext(cur))
2828+ {
2829+ osiSockAddrNode *pAddr = CONTAINER(cur, osiSockAddrNode, node);
2830+ status = sendto (beaconSocket, (char *)&msg, sizeof(msg), 0,
2831+ &pAddr->addr.sa, sizeof(pAddr->addr));
2832+ if (status < 0) {
2833 char sockErrBuf[64];
2834- epicsSocketConvertErrnoToString (
2835- sockErrBuf, sizeof ( sockErrBuf ) );
2836- ipAddrToDottedIP (&pNode->addr.ia, buf, sizeof(buf));
2837- errlogPrintf ( "%s: CA beacon routing (connect to \"%s\") error was \"%s\"\n",
2838+ epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
2839+ ipAddrToDottedIP (&pAddr->addr.ia, buf, sizeof(buf));
2840+ errlogPrintf ( "%s: CA beacon (send to \"%s\") error was \"%s\"\n",
2841 __FILE__, buf, sockErrBuf);
2842 }
2843 else {
2844- struct sockaddr_in if_addr;
2845-
2846- osiSocklen_t size = sizeof (if_addr);
2847- status = getsockname (sock, (struct sockaddr *) &if_addr, &size);
2848- if (status<0) {
2849- char sockErrBuf[64];
2850- epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
2851- errlogPrintf ( "%s: CA beacon routing (getsockname) error was \"%s\"\n",
2852- __FILE__, sockErrBuf);
2853- }
2854- else if (if_addr.sin_family==AF_INET) {
2855- msg.m_available = if_addr.sin_addr.s_addr;
2856- msg.m_cid = htonl ( beaconCounter );
2857-
2858- status = send (sock, (char *)&msg, sizeof(msg), 0);
2859- if (status < 0) {
2860- char sockErrBuf[64];
2861- epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
2862- ipAddrToDottedIP (&pNode->addr.ia, buf, sizeof(buf));
2863- errlogPrintf ( "%s: CA beacon (send to \"%s\") error was \"%s\"\n",
2864- __FILE__, buf, sockErrBuf);
2865- }
2866- else {
2867- assert (status == sizeof(msg));
2868- }
2869- }
2870+ assert (status == sizeof(msg));
2871 }
2872- pNode = (osiSockAddrNode *) pNode->node.next;
2873 }
2874
2875 epicsThreadSleep(delay);
2876
2877=== modified file 'src/ioc/rsrv/server.h'
2878--- src/ioc/rsrv/server.h 2014-03-06 21:55:13 +0000
2879+++ src/ioc/rsrv/server.h 2016-01-12 02:07:47 +0000
2880@@ -32,6 +32,7 @@
2881 #include "ellLib.h"
2882 #include "epicsTime.h"
2883 #include "epicsAssert.h"
2884+#include "osiSock.h"
2885
2886 #ifdef rsrvRestore_epicsExportSharedSymbols
2887 #define epicsExportSharedSymbols
2888@@ -88,7 +89,7 @@
2889 char *pUserName;
2890 char *pHostName;
2891 epicsEventId blockSem; /* used whenever the client blocks */
2892- SOCKET sock;
2893+ SOCKET sock, udpRecv;
2894 int proto;
2895 epicsThreadId tid;
2896 unsigned minor_version_number;
2897@@ -137,6 +138,15 @@
2898 char modified; /* mod & ev flw ctrl enbl */
2899 };
2900
2901+typedef struct {
2902+ ELLNODE node;
2903+ osiSockAddr tcpAddr, /* TCP listener endpoint */
2904+ udpAddr, /* UDP name unicast receiver endpoint */
2905+ udpbcastAddr; /* UDP name broadcast receiver endpoint */
2906+ SOCKET tcp, udp, udpbcast;
2907+
2908+ unsigned int startbcast:1;
2909+} rsrv_iface_config;
2910
2911 enum ctl {ctlInit, ctlRun, ctlPause, ctlExit};
2912
2913@@ -160,15 +170,15 @@
2914 #endif
2915
2916 GLBLTYPE int CASDEBUG;
2917-GLBLTYPE SOCKET IOC_sock;
2918-GLBLTYPE SOCKET IOC_cast_sock;
2919-GLBLTYPE unsigned short ca_server_port;
2920-GLBLTYPE ELLLIST clientQ; /* locked by clientQlock */
2921+GLBLTYPE unsigned short ca_server_port, ca_udp_port, ca_beacon_port;
2922+GLBLTYPE ELLLIST clientQ; /* (TCP clients) locked by clientQlock */
2923+GLBLTYPE ELLLIST clientQudp; /* locked by clientQlock */
2924+GLBLTYPE ELLLIST servers; /* rsrv_iface_config::node, read-only after rsrv_init() */
2925 GLBLTYPE ELLLIST beaconAddrList;
2926-GLBLTYPE ELLLIST casIntfAddrList;
2927+GLBLTYPE SOCKET beaconSocket;
2928+GLBLTYPE ELLLIST casIntfAddrList, casMCastAddrList;
2929 GLBLTYPE epicsMutexId clientQlock;
2930-GLBLTYPE struct client *prsrv_cast_client;
2931-GLBLTYPE BUCKET *pCaBucket;
2932+GLBLTYPE BUCKET *pCaBucket; /* locked by clientQlock */
2933 GLBLTYPE void *rsrvClientFreeList;
2934 GLBLTYPE void *rsrvChanFreeList;
2935 GLBLTYPE void *rsrvEventFreeList;
2936@@ -176,7 +186,7 @@
2937 GLBLTYPE void *rsrvLargeBufFreeListTCP;
2938 GLBLTYPE unsigned rsrvSizeofLargeBufTCP;
2939 GLBLTYPE void *rsrvPutNotifyFreeList;
2940-GLBLTYPE unsigned rsrvChannelCount;
2941+GLBLTYPE unsigned rsrvChannelCount; /* locked by clientQlock */
2942
2943 GLBLTYPE epicsEventId casudp_startStopEvent;
2944 GLBLTYPE epicsEventId beacon_startStopEvent;
2945@@ -185,6 +195,7 @@
2946 GLBLTYPE volatile enum ctl beacon_ctl;
2947 GLBLTYPE volatile enum ctl castcp_ctl;
2948
2949+GLBLTYPE unsigned int threadPrios[5];
2950
2951 #define CAS_HASH_TABLE_SIZE 4096
2952
2953
2954=== modified file 'src/libCom/env/envDefs.h'
2955--- src/libCom/env/envDefs.h 2015-06-29 22:45:18 +0000
2956+++ src/libCom/env/envDefs.h 2016-01-12 02:07:47 +0000
2957@@ -89,6 +89,8 @@
2958 envGetLongConfigParam(const ENV_PARAM *pParam, long *pLong);
2959 epicsShareFunc unsigned short epicsShareAPI envGetInetPortConfigParam
2960 (const ENV_PARAM *pEnv, unsigned short defaultPort);
2961+epicsShareFunc long epicsShareAPI
2962+ envGetBoolConfigParam(const ENV_PARAM *pParam, int *pBool);
2963 epicsShareFunc long epicsShareAPI epicsPrtEnvParams(void);
2964 epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *value);
2965 epicsShareFunc void epicsShareAPI epicsEnvShow (const char *name);
2966
2967=== modified file 'src/libCom/env/envSubr.c'
2968--- src/libCom/env/envSubr.c 2012-05-03 17:19:34 +0000
2969+++ src/libCom/env/envSubr.c 2016-01-12 02:07:47 +0000
2970@@ -43,6 +43,7 @@
2971 #define epicsExportSharedSymbols
2972 #include "epicsStdlib.h"
2973 #include "epicsStdio.h"
2974+#include "epicsString.h"
2975 #include "errMdef.h"
2976 #include "errlog.h"
2977 #include "envDefs.h"
2978@@ -319,7 +320,19 @@
2979 }
2980 return -1;
2981 }
2982-
2983
2984+
2985+
2986+long epicsShareAPI
2987+envGetBoolConfigParam(const ENV_PARAM *pParam, int *pBool)
2988+{
2989+ char text[20];
2990+
2991+ if(!envGetConfigParam(pParam, sizeof(text), text))
2992+ return -1;
2993+ *pBool = epicsStrCaseCmp(text, "yes")==0;
2994+ return 0;
2995+}
2996+
2997 /*+/subr**********************************************************************
2998 * NAME envPrtConfigParam - print value of a configuration parameter
2999 *
3000
3001=== modified file 'src/libCom/osi/Makefile'
3002--- src/libCom/osi/Makefile 2014-10-07 01:28:35 +0000
3003+++ src/libCom/osi/Makefile 2016-01-12 02:07:47 +0000
3004@@ -80,6 +80,10 @@
3005 Com_SRCS_vxWorks += osiNTPTime.c
3006 Com_SRCS_RTEMS += osiNTPTime.c
3007
3008+ifeq ($(OS_CLASS),vxWorks)
3009+osdTime.o: USR_CXXFLAGS += -DBUILD_TIME=$(shell perl -e 'print time')
3010+endif
3011+
3012 Com_SRCS += osdSock.c
3013 Com_SRCS += osdSockAddrReuse.cpp
3014 Com_SRCS += osiSock.c
3015
3016=== modified file 'src/libCom/osi/os/Darwin/osdSock.h'
3017--- src/libCom/osi/os/Darwin/osdSock.h 2011-09-09 22:14:35 +0000
3018+++ src/libCom/osi/os/Darwin/osdSock.h 2016-01-12 02:07:47 +0000
3019@@ -41,6 +41,7 @@
3020 #define SOCK_ECONNRESET ECONNRESET
3021 #define SOCK_ETIMEDOUT ETIMEDOUT
3022 #define SOCK_EADDRINUSE EADDRINUSE
3023+#define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL
3024 #define SOCK_ECONNREFUSED ECONNREFUSED
3025 #define SOCK_ECONNABORTED ECONNABORTED
3026 #define SOCK_EINPROGRESS EINPROGRESS
3027
3028=== modified file 'src/libCom/osi/os/Linux/osdSock.h'
3029--- src/libCom/osi/os/Linux/osdSock.h 2014-09-08 22:57:18 +0000
3030+++ src/libCom/osi/os/Linux/osdSock.h 2016-01-12 02:07:47 +0000
3031@@ -43,6 +43,7 @@
3032 #define SOCK_ECONNRESET ECONNRESET
3033 #define SOCK_ETIMEDOUT ETIMEDOUT
3034 #define SOCK_EADDRINUSE EADDRINUSE
3035+#define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL
3036 #define SOCK_ECONNREFUSED ECONNREFUSED
3037 #define SOCK_ECONNABORTED ECONNABORTED
3038 #define SOCK_EINPROGRESS EINPROGRESS
3039
3040=== modified file 'src/libCom/osi/os/RTEMS/osdSock.h'
3041--- src/libCom/osi/os/RTEMS/osdSock.h 2011-09-09 22:14:35 +0000
3042+++ src/libCom/osi/os/RTEMS/osdSock.h 2016-01-12 02:07:47 +0000
3043@@ -51,6 +51,7 @@
3044 #define SOCK_ECONNRESET ECONNRESET
3045 #define SOCK_ETIMEDOUT ETIMEDOUT
3046 #define SOCK_EADDRINUSE EADDRINUSE
3047+#define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL
3048 #define SOCK_ECONNREFUSED ECONNREFUSED
3049 #define SOCK_ECONNABORTED ECONNABORTED
3050 #define SOCK_EINPROGRESS EINPROGRESS
3051
3052=== modified file 'src/libCom/osi/os/WIN32/osdSock.h'
3053--- src/libCom/osi/os/WIN32/osdSock.h 2011-09-09 22:14:35 +0000
3054+++ src/libCom/osi/os/WIN32/osdSock.h 2016-01-12 02:07:47 +0000
3055@@ -49,6 +49,7 @@
3056 #define SOCK_ECONNRESET WSAECONNRESET
3057 #define SOCK_ETIMEDOUT WSAETIMEDOUT
3058 #define SOCK_EADDRINUSE WSAEADDRINUSE
3059+#define SOCK_EADDRNOTAVAIL WSAEADDRNOTAVAIL
3060 #define SOCK_ECONNREFUSED WSAECONNREFUSED
3061 #define SOCK_ECONNABORTED WSAECONNABORTED
3062 #define SOCK_EINPROGRESS WSAEINPROGRESS
3063
3064=== modified file 'src/libCom/osi/os/cygwin32/osdSock.h'
3065--- src/libCom/osi/os/cygwin32/osdSock.h 2015-04-29 19:01:07 +0000
3066+++ src/libCom/osi/os/cygwin32/osdSock.h 2016-01-12 02:07:47 +0000
3067@@ -50,6 +50,7 @@
3068 #define SOCK_ECONNRESET ECONNRESET
3069 #define SOCK_ETIMEDOUT ETIMEDOUT
3070 #define SOCK_EADDRINUSE EADDRINUSE
3071+#define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL
3072 #define SOCK_ECONNREFUSED ECONNREFUSED
3073 #define SOCK_ECONNABORTED ECONNABORTED
3074 #define SOCK_EINPROGRESS EINPROGRESS
3075
3076=== modified file 'src/libCom/osi/os/default/epicsMMIODef.h'
3077--- src/libCom/osi/os/default/epicsMMIODef.h 2014-09-26 21:40:44 +0000
3078+++ src/libCom/osi/os/default/epicsMMIODef.h 2016-01-12 02:07:47 +0000
3079@@ -13,6 +13,7 @@
3080
3081 #include <epicsTypes.h>
3082 #include <epicsEndian.h>
3083+#include <compilerSpecific.h>
3084 #include <shareLib.h>
3085
3086 #ifdef __cplusplus
3087@@ -27,7 +28,7 @@
3088
3089 /** @brief Read a single byte.
3090 */
3091-INLINE
3092+static EPICS_ALWAYS_INLINE
3093 epicsUInt8
3094 ioread8(volatile void* addr)
3095 {
3096@@ -36,7 +37,7 @@
3097
3098 /** @brief Write a single byte.
3099 */
3100-INLINE
3101+static EPICS_ALWAYS_INLINE
3102 void
3103 iowrite8(volatile void* addr, epicsUInt8 val)
3104 {
3105@@ -46,7 +47,7 @@
3106 /** @brief Read two bytes in host order.
3107 * Not byte swapping
3108 */
3109-INLINE
3110+static EPICS_ALWAYS_INLINE
3111 epicsUInt16
3112 nat_ioread16(volatile void* addr)
3113 {
3114@@ -56,7 +57,7 @@
3115 /** @brief Write two byte in host order.
3116 * Not byte swapping
3117 */
3118-INLINE
3119+static EPICS_ALWAYS_INLINE
3120 void
3121 nat_iowrite16(volatile void* addr, epicsUInt16 val)
3122 {
3123@@ -66,7 +67,7 @@
3124 /** @brief Read four bytes in host order.
3125 * Not byte swapping
3126 */
3127-INLINE
3128+static EPICS_ALWAYS_INLINE
3129 epicsUInt32
3130 nat_ioread32(volatile void* addr)
3131 {
3132@@ -76,7 +77,7 @@
3133 /** @brief Write four byte in host order.
3134 * Not byte swapping
3135 */
3136-INLINE
3137+static EPICS_ALWAYS_INLINE
3138 void
3139 nat_iowrite32(volatile void* addr, epicsUInt32 val)
3140 {
3141
3142=== modified file 'src/libCom/osi/os/default/osdNetIntf.c'
3143--- src/libCom/osi/os/default/osdNetIntf.c 2015-08-18 19:46:22 +0000
3144+++ src/libCom/osi/os/default/osdNetIntf.c 2016-01-12 02:07:47 +0000
3145@@ -77,21 +77,6 @@
3146 struct ifreq *pifreq;
3147 struct ifreq *pnextifreq;
3148 osiSockAddrNode *pNewNode;
3149-
3150- if ( pMatchAddr->sa.sa_family == AF_INET ) {
3151- if ( pMatchAddr->ia.sin_addr.s_addr == htonl (INADDR_LOOPBACK) ) {
3152- pNewNode = (osiSockAddrNode *) calloc (1, sizeof (*pNewNode) );
3153- if ( pNewNode == NULL ) {
3154- errlogPrintf ( "osiSockDiscoverBroadcastAddresses(): no memory available for configuration\n" );
3155- return;
3156- }
3157- pNewNode->addr.ia.sin_family = AF_INET;
3158- pNewNode->addr.ia.sin_port = htons ( 0 );
3159- pNewNode->addr.ia.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
3160- ellAdd ( pList, &pNewNode->node );
3161- return;
3162- }
3163- }
3164
3165 /*
3166 * use pool so that we avoid using too much stack space
3167@@ -120,6 +105,7 @@
3168
3169 for ( pifreq = pIfreqList; pifreq <= pIfreqListEnd; pifreq = pnextifreq ) {
3170 uint32_t current_ifreqsize;
3171+ struct sockaddr_in if_addr;
3172
3173 /*
3174 * find the next ifreq
3175@@ -143,6 +129,7 @@
3176 ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): interface \"%s\" was not AF_INET\n", pIfreqList->ifr_name) );
3177 continue;
3178 }
3179+ if_addr = *(struct sockaddr_in *)&pIfreqList->ifr_addr;
3180
3181 /*
3182 * if it isnt a wildcarded interface then look for
3183@@ -153,8 +140,7 @@
3184 continue;
3185 }
3186 if ( pMatchAddr->ia.sin_addr.s_addr != htonl (INADDR_ANY) ) {
3187- struct sockaddr_in *pInetAddr = (struct sockaddr_in *) &pIfreqList->ifr_addr;
3188- if ( pInetAddr->sin_addr.s_addr != pMatchAddr->ia.sin_addr.s_addr ) {
3189+ if ( if_addr.sin_addr.s_addr != pMatchAddr->ia.sin_addr.s_addr ) {
3190 ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" didnt match\n", pIfreqList->ifr_name) );
3191 continue;
3192 }
3193@@ -175,14 +161,6 @@
3194 continue;
3195 }
3196
3197- /*
3198- * dont use the loop back interface
3199- */
3200- if ( pIfreqList->ifr_flags & IFF_LOOPBACK ) {
3201- ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): ignoring loopback interface: \"%s\"\n", pIfreqList->ifr_name) );
3202- continue;
3203- }
3204-
3205 pNewNode = (osiSockAddrNode *) calloc (1, sizeof (*pNewNode) );
3206 if ( pNewNode == NULL ) {
3207 errlogPrintf ( "osiSockDiscoverBroadcastAddresses(): no memory available for configuration\n" );
3208@@ -221,6 +199,22 @@
3209 pNewNode->addr.sa = pIfreqList->ifr_dstaddr;
3210 }
3211 #endif
3212+#if defined(__linux__)
3213+ /* On Linux, even though the 'lo' interface doesn't set IFF_BROADCAST
3214+ * a broadcast route often exists. Assume that sending to 127.255.255.255
3215+ * reaches all local listeners.
3216+ *
3217+ * $ ip route show table local scope link dev lo
3218+ * broadcast 127.0.0.0 proto kernel src 127.0.0.1
3219+ * broadcast 127.255.255.255 proto kernel src 127.0.0.1
3220+ */
3221+ else if ( pIfreqList->ifr_flags & IFF_LOOPBACK && if_addr.sin_addr.s_addr == htonl(INADDR_LOOPBACK) ) {
3222+ memset(&pNewNode->addr.ia, 0, sizeof(pNewNode->addr.ia));
3223+ pNewNode->addr.ia.sin_family = AF_INET;
3224+ pNewNode->addr.ia.sin_addr.s_addr = htonl(0x7fffffff);
3225+ ifDepenDebugPrintf ( ( "assume loopback broadcast addr = %x\n", ntohl ( pNewNode->addr.ia.sin_addr.s_addr ) ) );
3226+ }
3227+#endif
3228 else {
3229 ifDepenDebugPrintf ( ( "osiSockDiscoverBroadcastAddresses(): net intf \"%s\": not point to point or bcast?\n", pIfreqList->ifr_name ) );
3230 free ( pNewNode );
3231
3232=== modified file 'src/libCom/osi/os/freebsd/osdSock.h'
3233--- src/libCom/osi/os/freebsd/osdSock.h 2011-09-09 22:14:35 +0000
3234+++ src/libCom/osi/os/freebsd/osdSock.h 2016-01-12 02:07:47 +0000
3235@@ -44,6 +44,7 @@
3236 #define SOCK_ECONNRESET ECONNRESET
3237 #define SOCK_ETIMEDOUT ETIMEDOUT
3238 #define SOCK_EADDRINUSE EADDRINUSE
3239+#define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL
3240 #define SOCK_ECONNREFUSED ECONNREFUSED
3241 #define SOCK_ECONNABORTED ECONNABORTED
3242 #define SOCK_EINPROGRESS EINPROGRESS
3243
3244=== modified file 'src/libCom/osi/os/iOS/osdSock.h'
3245--- src/libCom/osi/os/iOS/osdSock.h 2011-09-09 22:14:35 +0000
3246+++ src/libCom/osi/os/iOS/osdSock.h 2016-01-12 02:07:47 +0000
3247@@ -42,6 +42,7 @@
3248 #define SOCK_ECONNRESET ECONNRESET
3249 #define SOCK_ETIMEDOUT ETIMEDOUT
3250 #define SOCK_EADDRINUSE EADDRINUSE
3251+#define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL
3252 #define SOCK_ECONNREFUSED ECONNREFUSED
3253 #define SOCK_ECONNABORTED ECONNABORTED
3254 #define SOCK_EINPROGRESS EINPROGRESS
3255
3256=== modified file 'src/libCom/osi/os/solaris/osdSock.h'
3257--- src/libCom/osi/os/solaris/osdSock.h 2011-09-09 22:14:35 +0000
3258+++ src/libCom/osi/os/solaris/osdSock.h 2016-01-12 02:07:47 +0000
3259@@ -52,6 +52,7 @@
3260 #define SOCK_ECONNRESET ECONNRESET
3261 #define SOCK_ETIMEDOUT ETIMEDOUT
3262 #define SOCK_EADDRINUSE EADDRINUSE
3263+#define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL
3264 #define SOCK_ECONNREFUSED ECONNREFUSED
3265 #define SOCK_ECONNABORTED ECONNABORTED
3266 #define SOCK_EINPROGRESS EINPROGRESS
3267
3268=== modified file 'src/libCom/osi/os/vxWorks/epicsMMIO.h'
3269--- src/libCom/osi/os/vxWorks/epicsMMIO.h 2014-08-01 21:06:17 +0000
3270+++ src/libCom/osi/os/vxWorks/epicsMMIO.h 2016-01-12 02:07:47 +0000
3271@@ -83,13 +83,18 @@
3272 * They do *not* all implement the sys{In/Out}{Byte/Word/Long}
3273 * functions to do the same thing though, so we can't use them.
3274 */
3275-
3276+#ifdef __cplusplus
3277+extern "C" {
3278+#endif
3279 UINT8 sysPciInByte(UINT8 *addr);
3280 void sysPciOutByte(UINT8 *addr, UINT8 data);
3281 UINT16 sysPciInWord(UINT16 *addr);
3282 void sysPciOutWord(UINT16 *addr, UINT16 data);
3283 UINT32 sysPciInLong (UINT32 *addr);
3284 void sysPciOutLong (UINT32 *addr, UINT32 data);
3285+#ifdef __cplusplus
3286+}
3287+#endif
3288
3289 #define ioread8(address) sysPciInByte((UINT8 *)(address))
3290 #define iowrite8(address,data) sysPciOutByte((UINT8 *)(address), (epicsUInt8)(data))
3291
3292=== modified file 'src/libCom/osi/os/vxWorks/osdSock.h'
3293--- src/libCom/osi/os/vxWorks/osdSock.h 2011-09-20 18:07:52 +0000
3294+++ src/libCom/osi/os/vxWorks/osdSock.h 2016-01-12 02:07:47 +0000
3295@@ -73,6 +73,7 @@
3296 #define SOCK_ECONNRESET ECONNRESET
3297 #define SOCK_ETIMEDOUT ETIMEDOUT
3298 #define SOCK_EADDRINUSE EADDRINUSE
3299+#define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL
3300 #define SOCK_ECONNREFUSED ECONNREFUSED
3301 #define SOCK_ECONNABORTED ECONNABORTED
3302 #define SOCK_EINPROGRESS EINPROGRESS
3303
3304=== modified file 'src/libCom/osi/os/vxWorks/osdTime.cpp'
3305--- src/libCom/osi/os/vxWorks/osdTime.cpp 2013-06-07 19:35:45 +0000
3306+++ src/libCom/osi/os/vxWorks/osdTime.cpp 2016-01-12 02:07:47 +0000
3307@@ -1,5 +1,5 @@
3308 /*************************************************************************\
3309-* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
3310+* Copyright (c) 2015 UChicago Argonne LLC, as Operator of Argonne
3311 * National Laboratory.
3312 * Copyright (c) 2002 The Regents of the University of California, as
3313 * Operator of Los Alamos National Laboratory.
3314@@ -7,11 +7,14 @@
3315 * in file LICENSE that is included with this distribution.
3316 \*************************************************************************/
3317
3318+#define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h>
3319+
3320 #include <vxWorks.h>
3321 #include <sntpcLib.h>
3322 #include <string.h>
3323 #include <stdio.h>
3324 #include <stdlib.h>
3325+#include <taskLib.h>
3326
3327 #include "epicsTime.h"
3328 #include "osiNTPTime.h"
3329@@ -21,6 +24,9 @@
3330
3331 #define NTP_REQUEST_TIMEOUT 4 /* seconds */
3332
3333+static char sntp_sync_task[] = "ipsntps";
3334+static char ntp_daemon[] = "ipntpd";
3335+
3336 static const char *pserverAddr = NULL;
3337 extern char* sysBootLine;
3338
3339@@ -30,18 +36,40 @@
3340 if (getenv("TIMEZONE") == NULL) {
3341 const char *timezone = envGetConfigParamPtr(&EPICS_TIMEZONE);
3342 if (timezone == NULL) {
3343- printf("NTPTime_Init: No Time Zone Information\n");
3344+ printf("timeRegister: No Time Zone Information\n");
3345 } else {
3346 epicsEnvSet("TIMEZONE", timezone);
3347 }
3348 }
3349
3350- NTPTime_Init(100); /* init NTP first so it can be used to sync SysTime */
3351- ClockTime_Init(CLOCKTIME_SYNC);
3352+ // Define EPICS_TS_FORCE_NTPTIME to force use of NTPTime provider
3353+ bool useNTP = getenv("EPICS_TS_FORCE_NTPTIME") != NULL;
3354+
3355+ if (!useNTP &&
3356+ (taskNameToId(sntp_sync_task) != ERROR ||
3357+ taskNameToId(ntp_daemon) != ERROR)) {
3358+ // A VxWorks 6 SNTP/NTP sync task is running
3359+ struct timespec clockNow;
3360+
3361+ useNTP = clock_gettime(CLOCK_REALTIME, &clockNow) != OK ||
3362+ clockNow.tv_sec < BUILD_TIME;
3363+ // Assumes VxWorks and the host OS have the same epoch
3364+ }
3365+
3366+ if (useNTP) {
3367+ // Start NTP first so it can be used to sync SysTime
3368+ NTPTime_Init(100);
3369+ ClockTime_Init(CLOCKTIME_SYNC);
3370+ } else {
3371+ ClockTime_Init(CLOCKTIME_NOSYNC);
3372+ }
3373 return 1;
3374 }
3375 static int done = timeRegister();
3376
3377+
3378+// Routines for NTPTime provider
3379+
3380 int osdNTPGet(struct timespec *ts)
3381 {
3382 return sntpcTimeGet(const_cast<char *>(pserverAddr),
3383@@ -54,6 +82,7 @@
3384 if (!pserverAddr) { /* use the boot host */
3385 BOOT_PARAMS bootParms;
3386 static char host_addr[BOOT_ADDR_LEN];
3387+
3388 bootStringToStruct(sysBootLine, &bootParms);
3389 /* bootParms.had = host IP address */
3390 strncpy(host_addr, bootParms.had, BOOT_ADDR_LEN);
3391@@ -63,10 +92,13 @@
3392
3393 void osdNTPReport(void)
3394 {
3395- printf("NTP Server = %s\n", pserverAddr);
3396+ if (pserverAddr)
3397+ printf("NTP Server = %s\n", pserverAddr);
3398 }
3399
3400
3401+// Other Time Routines
3402+
3403 // vxWorks localtime_r returns different things in different versions.
3404 // It can't fail though, so we just ignore the return value.
3405 int epicsTime_localtime(const time_t *clock, struct tm *result)
3406
3407=== modified file 'src/libCom/osi/osiClockTime.c'
3408--- src/libCom/osi/osiClockTime.c 2015-09-07 14:50:48 +0000
3409+++ src/libCom/osi/osiClockTime.c 2016-01-12 02:07:47 +0000
3410@@ -70,37 +70,54 @@
3411
3412 /* Initialization */
3413
3414-static void ClockTime_InitOnce(void *psync)
3415+static void ClockTime_InitOnce(void *pfirst)
3416 {
3417- ClockTimePvt.synchronize = *(int *)psync;
3418+ *(int *) pfirst = 1;
3419+
3420 ClockTimePvt.loopEvent = epicsEventMustCreate(epicsEventEmpty);
3421 ClockTimePvt.lock = epicsMutexCreate();
3422
3423- if (ClockTimePvt.synchronize) {
3424- /* Start the sync thread */
3425- epicsThreadCreate("ClockTimeSync", epicsThreadPriorityHigh,
3426- epicsThreadGetStackSize(epicsThreadStackSmall),
3427- ClockTimeSync, NULL);
3428- }
3429- else {
3430- ClockTimeGetCurrent(&ClockTimePvt.startTime);
3431- }
3432-
3433 epicsAtExit(ClockTime_Shutdown, NULL);
3434
3435 /* Register the iocsh commands */
3436 iocshRegister(&ReportFuncDef, ReportCallFunc);
3437- if (ClockTimePvt.synchronize)
3438- iocshRegister(&ShutdownFuncDef, ShutdownCallFunc);
3439+ iocshRegister(&ShutdownFuncDef, ShutdownCallFunc);
3440
3441- /* Finally register as a time provider */
3442+ /* Register as a time provider */
3443 generalTimeRegisterCurrentProvider("OS Clock", LAST_RESORT_PRIORITY,
3444 ClockTimeGetCurrent);
3445 }
3446
3447 void ClockTime_Init(int synchronize)
3448 {
3449- epicsThreadOnce(&onceId, ClockTime_InitOnce, &synchronize);
3450+ int firstTime = 0;
3451+
3452+ epicsThreadOnce(&onceId, ClockTime_InitOnce, &firstTime);
3453+
3454+ if (synchronize == CLOCKTIME_SYNC) {
3455+ if (ClockTimePvt.synchronize == CLOCKTIME_NOSYNC) {
3456+ /* Start synchronizing */
3457+ ClockTimePvt.synchronize = synchronize;
3458+
3459+ epicsThreadCreate("ClockTimeSync", epicsThreadPriorityHigh,
3460+ epicsThreadGetStackSize(epicsThreadStackSmall),
3461+ ClockTimeSync, NULL);
3462+ }
3463+ else {
3464+ /* No change, sync thread should already be running */
3465+ }
3466+ }
3467+ else {
3468+ if (ClockTimePvt.synchronize == CLOCKTIME_SYNC) {
3469+ /* Turn off synchronization thread */
3470+ ClockTime_Shutdown(NULL);
3471+ }
3472+ else {
3473+ /* No synchronization thread */
3474+ if (firstTime)
3475+ ClockTimeGetCurrent(&ClockTimePvt.startTime);
3476+ }
3477+ }
3478 }
3479
3480
3481@@ -108,7 +125,7 @@
3482
3483 void ClockTime_Shutdown(void *dummy)
3484 {
3485- ClockTimePvt.synchronize = 0;
3486+ ClockTimePvt.synchronize = CLOCKTIME_NOSYNC;
3487 epicsEventSignal(ClockTimePvt.loopEvent);
3488 }
3489
3490@@ -126,7 +143,7 @@
3491
3492 for (epicsEventWaitWithTimeout(ClockTimePvt.loopEvent,
3493 ClockTimeSyncInterval);
3494- ClockTimePvt.synchronize;
3495+ ClockTimePvt.synchronize == CLOCKTIME_SYNC;
3496 epicsEventWaitWithTimeout(ClockTimePvt.loopEvent,
3497 ClockTimeSyncInterval)) {
3498 epicsTimeStamp timeNow;
3499@@ -144,8 +161,8 @@
3500
3501 epicsMutexMustLock(ClockTimePvt.lock);
3502 if (!ClockTimePvt.synchronized) {
3503- ClockTimePvt.startTime = timeNow;
3504- ClockTimePvt.synchronized = 1;
3505+ ClockTimePvt.startTime = timeNow;
3506+ ClockTimePvt.synchronized = 1;
3507 }
3508 ClockTimePvt.syncFromPriority = priority;
3509 ClockTimePvt.syncTime = timeNow;
3510@@ -200,29 +217,39 @@
3511 #ifdef CLOCK_REALTIME
3512 "initialized"
3513 #else
3514- "included"
3515+ "available"
3516 #endif /* CLOCK_REALTIME */
3517 );
3518- } else if (ClockTimePvt.synchronize) {
3519+ }
3520+ else if (ClockTimePvt.synchronize == CLOCKTIME_SYNC) {
3521+ int synchronized, syncFromPriority;
3522+ epicsTimeStamp startTime, syncTime;
3523+
3524 epicsMutexMustLock(ClockTimePvt.lock);
3525- if (ClockTimePvt.synchronized) {
3526- printf("OS Clock driver has synchronized to a priority=%d provider\n",
3527- ClockTimePvt.syncFromPriority);
3528+ synchronized = ClockTimePvt.synchronized;
3529+ syncFromPriority = ClockTimePvt.syncFromPriority;
3530+ startTime = ClockTimePvt.startTime;
3531+ syncTime = ClockTimePvt.syncTime;
3532+ epicsMutexUnlock(ClockTimePvt.lock);
3533+
3534+ if (synchronized) {
3535+ printf("OS Clock driver is synchronized to a priority=%d provider\n",
3536+ syncFromPriority);
3537 if (level) {
3538 epicsTimeToStrftime(timebuf, sizeof(timebuf),
3539- "%Y-%m-%d %H:%M:%S.%06f", &ClockTimePvt.startTime);
3540+ "%Y-%m-%d %H:%M:%S.%06f", &startTime);
3541 printf("Initial sync was at %s\n", timebuf);
3542 epicsTimeToStrftime(timebuf, sizeof(timebuf),
3543- "%Y-%m-%d %H:%M:%S.%06f", &ClockTimePvt.syncTime);
3544+ "%Y-%m-%d %H:%M:%S.%06f", &syncTime);
3545 printf("Last successful sync was at %s\n", timebuf);
3546 }
3547 printf("Syncronization interval = %.0f seconds\n",
3548 ClockTimeSyncInterval);
3549- } else
3550- printf("OS Clock driver has *not* yet synchronized\n");
3551-
3552- epicsMutexUnlock(ClockTimePvt.lock);
3553- } else {
3554+ }
3555+ else
3556+ printf("OS Clock driver is *not* synchronized\n");
3557+ }
3558+ else {
3559 epicsTimeToStrftime(timebuf, sizeof(timebuf),
3560 "%Y-%m-%d %H:%M:%S.%06f", &ClockTimePvt.startTime);
3561 printf("Program started at %s\n", timebuf);
3562
3563=== modified file 'src/std/filters/filters.dbd.pod'
3564--- src/std/filters/filters.dbd.pod 2014-10-03 19:23:04 +0000
3565+++ src/std/filters/filters.dbd.pod 2016-01-12 02:07:47 +0000
3566@@ -6,13 +6,13 @@
3567
3568 =over
3569
3570-=item * L<TimeStamp|/"TimeStamp Filter "ts"">
3571-
3572-=item * L<Deadband|/"Deadband Filter "dbnd"">
3573-
3574-=item * L<Array|/"Array Filter "arr"">
3575-
3576-=item * L<Synchronize|/"Synchronize Filter "sync"">
3577+=item * L<TimeStamp|/"TimeStamp Filter ts">
3578+
3579+=item * L<Deadband|/"Deadband Filter dbnd">
3580+
3581+=item * L<Array|/"Array Filter arr">
3582+
3583+=item * L<Synchronize|/"Synchronize Filter sync">
3584
3585 =back
3586
3587
3588=== modified file 'src/tools/Makefile'
3589--- src/tools/Makefile 2015-07-10 19:02:01 +0000
3590+++ src/tools/Makefile 2016-01-12 02:07:47 +0000
3591@@ -33,6 +33,7 @@
3592 PERL_MODULES += DBD/Registrar.pm
3593 PERL_MODULES += DBD/Variable.pm
3594
3595+PERL_SCRIPTS += assembleSnippets.pl
3596 PERL_SCRIPTS += convertRelease.pl
3597 PERL_SCRIPTS += cvsclean.pl
3598 PERL_SCRIPTS += dos2unix.pl
3599
3600=== added file 'src/tools/assembleSnippets.pl'
3601--- src/tools/assembleSnippets.pl 1970-01-01 00:00:00 +0000
3602+++ src/tools/assembleSnippets.pl 2016-01-12 02:07:47 +0000
3603@@ -0,0 +1,151 @@
3604+#!/usr/bin/env perl
3605+#*************************************************************************
3606+# Copyright (c) 2015 ITER Organization.
3607+# EPICS BASE is distributed subject to a Software License Agreement found
3608+# in file LICENSE that is included with this distribution.
3609+#*************************************************************************
3610+
3611+# $Id$
3612+
3613+use strict;
3614+use warnings;
3615+
3616+use Getopt::Std;
3617+use Sys::Hostname;
3618+use File::Basename;
3619+use Data::Dumper;
3620+
3621+our ($opt_o, $opt_d, $opt_m, $opt_i, $opt_M);
3622+
3623+$Getopt::Std::OUTPUT_HELP_VERSION = 1;
3624+&HELP_MESSAGE if !getopts('M:i:m:o:d') || @ARGV == 0;
3625+
3626+my $out;
3627+my $dep;
3628+my %snippets;
3629+my $ipattern;
3630+
3631+my $datetime = localtime();
3632+my $user = $ENV{LOGNAME} || $ENV{USER} || $ENV{USERNAME};
3633+my $host = hostname;
3634+my %replacements = (
3635+ _DATETIME_ => $datetime,
3636+ _USERNAME_ => $user,
3637+ _HOST_ => $host,
3638+);
3639+
3640+if ($opt_o) {
3641+ open $out, '>', $opt_o or
3642+ die "Can't create $opt_o: $!\n";
3643+ print STDERR "opened file $opt_o for output\n" if $opt_d;
3644+ $replacements{_OUTPUTFILE_} = $opt_o;
3645+} else {
3646+ open $out, '>&', STDOUT;
3647+ print STDERR "using STDOUT for output\n" if $opt_d;
3648+ $replacements{_OUTPUTFILE_} = 'STDERR';
3649+}
3650+
3651+if ($opt_m) {
3652+ foreach my $r (split /,/, $opt_m) {
3653+ (my $k, my $v) = split /=/, $r;
3654+ $replacements{$k} = $v;
3655+ }
3656+}
3657+
3658+if ($opt_M) {
3659+ open $dep, '>', $opt_M or
3660+ die "Can't create $opt_M: $!\n";
3661+ print STDERR "opened dependency file $opt_M for output\n" if $opt_d;
3662+ print $dep basename($opt_o), ":";
3663+}
3664+
3665+if ($opt_i) {
3666+ $ipattern = qr($opt_i);
3667+}
3668+
3669+# %snippets is a hash {rank}
3670+# of hashes {name-after-rank}
3671+# of arrays[] [files...]
3672+# of arrays[2] [filename, command]
3673+print STDERR "reading input files\n" if $opt_d;
3674+foreach (@ARGV) {
3675+ my $name = basename($_);
3676+ if ($opt_i and not $name =~ /$ipattern/) {
3677+ print STDERR " snippet $_ does not match input pattern $opt_i - ignoring\n" if $opt_d;
3678+ next;
3679+ }
3680+ if ($name =~ /\A([ARD]?)([0-9]+)(.*[^~])\z/) {
3681+ print STDERR " considering snippet $_\n" if $opt_d;
3682+ if (exists $snippets{$2}) {
3683+ my %rank = %{$snippets{$2}};
3684+ my @files = @{ $rank{(keys %rank)[0]} };
3685+ my $existcmd = $files[0]->[1];
3686+ if ($1 eq "D" and $existcmd ne "D") {
3687+ print STDERR " ignoring 'D' default for existing rank $2\n" if $opt_d;
3688+ next;
3689+ } elsif ($1 eq "R") {
3690+ print STDERR " 'R' command - deleting existing rank $2 snippets\n" if $opt_d;
3691+ $snippets{$2} = {};
3692+ } elsif ($existcmd eq "D") {
3693+ print STDERR " deleting existing rank $2 default snippet\n" if $opt_d;
3694+ $snippets{$2} = {};
3695+ }
3696+ }
3697+ if ($opt_d) {
3698+ print STDERR " adding snippet ";
3699+ print STDERR "marked as default " if $1 eq "D";
3700+ print STDERR "to rank $2\n";
3701+ }
3702+ $snippets{$2}{$3} = () if (not exists $snippets{$2}{$3});
3703+ push @{$snippets{$2}{$3}}, [ $_, $1 ];
3704+ }
3705+}
3706+
3707+if ($opt_d) {
3708+ print STDERR "finished reading input files\n";
3709+ print STDERR "dumping the final snippet structure\n";
3710+ print STDERR Dumper(\%snippets);
3711+ print STDERR "dumping the macro replacements\n";
3712+ print STDERR Dumper(\%replacements);
3713+ print STDERR "creating output\n";
3714+}
3715+
3716+foreach my $r (sort {$a<=>$b} keys %snippets) {
3717+ print STDERR " working on rank $r\n" if $opt_d;
3718+ foreach my $n (sort keys %{$snippets{$r}}) {
3719+ foreach my $s (@{$snippets{$r}{$n}}) {
3720+ my $in;
3721+ my $f = $s->[0];
3722+ print STDERR " snippet $n from file $f\n" if $opt_d;
3723+ open $in, '<', $f or die "Can't open $f: $!\n";
3724+ $replacements{_SNIPPETFILE_} = $f;
3725+ print $dep " \\\n $f" if $opt_M;
3726+ while (<$in>) {
3727+ chomp;
3728+ foreach my $k (keys %replacements) {
3729+ s/$k/$replacements{$k}/g;
3730+ }
3731+ print $out $_, "\n";
3732+ }
3733+ close $in;
3734+ }
3735+ }
3736+}
3737+
3738+print STDERR "finished creating output, closing\n" if $opt_d;
3739+if ($opt_M) {
3740+ print $dep "\n";
3741+ close $dep;
3742+}
3743+close $out;
3744+
3745+sub HELP_MESSAGE {
3746+ print STDERR "Usage: assembleSnippets.pl [options] snippets ...\n";
3747+ print STDERR "Options:\n";
3748+ print STDERR " -o file output file [STDOUT]\n";
3749+ print STDERR " -d debug mode [no]\n";
3750+ print STDERR " -m macros list of macro replacements as \"key=val,key=val\"\n";
3751+ print STDERR " -i pattern pattern for input files to match\n";
3752+ print STDERR " -M file write file with dependency rule suitable for make\n";
3753+ exit 2;
3754+}
3755
3756=== modified file 'src/tools/test/Makefile'
3757--- src/tools/test/Makefile 2012-03-14 20:27:40 +0000
3758+++ src/tools/test/Makefile 2016-01-12 02:07:47 +0000
3759@@ -18,6 +18,7 @@
3760 TESTS += Recfield
3761 TESTS += Recordtype
3762 TESTS += Registrar
3763+TESTS += Snippets
3764 TESTS += Variable
3765
3766 TESTSCRIPTS_HOST += $(TESTS:%=%.t)
3767
3768=== added file 'src/tools/test/Snippets.plt'
3769--- src/tools/test/Snippets.plt 1970-01-01 00:00:00 +0000
3770+++ src/tools/test/Snippets.plt 2016-01-12 02:07:47 +0000
3771@@ -0,0 +1,110 @@
3772+#!/usr/bin/env perl
3773+
3774+use File::Path;
3775+use Sys::Hostname;
3776+
3777+use Test::More tests => 29;
3778+
3779+my $user = $ENV{LOGNAME} || $ENV{USER} || $ENV{USERNAME};
3780+my $host = hostname;
3781+
3782+mkdir "a$$";
3783+mkdir "b$$";
3784+
3785+sub mksnip {
3786+ my ($dir, $file, $line) = @_;
3787+ open(my $fh, '>', "$dir$$/$file") or die "can't open $dir$$/$file : $!";
3788+ print $fh $line;
3789+ close $fh;
3790+}
3791+
3792+sub assemble {
3793+ my @cmd = ( 'perl', '../../assembleSnippets.pl', '-o', "out$$" );
3794+ push @cmd, @_;
3795+ system(@cmd);
3796+ open(my $fh, '<', "out$$") or die "can't open out$$ : $!";
3797+ chomp(my @result = <$fh>);
3798+ close $fh;
3799+ return join (' ', @result);
3800+}
3801+
3802+# Adding two snippets of same rank, sorting alphabetically
3803+mksnip('a', '10_a', '10');
3804+mksnip('b', '10_c', '12');
3805+is assemble("a$$/10_a", "b$$/10_c"), '10 12', "adding same rank; ordered";
3806+is assemble("b$$/10_c", "a$$/10_a"), '10 12', "adding same rank; reverse order";
3807+
3808+# Same, with 'A' cmd
3809+mksnip('a', 'A10_a', '10');
3810+mksnip('b', 'A10_c', '12');
3811+is assemble("a$$/10_a", "b$$/A10_c"), '10 12', "'A' add same rank; ordered";
3812+is assemble("b$$/10_c", "a$$/A10_a"), '10 12', "'A' add same rank; reverse order";
3813+
3814+# Same name does not create issues
3815+mksnip('b', '10_a', '10x');
3816+is assemble("a$$/10_a", "b$$/10_a"), '10 10x', "adding same name twice; order a-b";
3817+is assemble("b$$/10_a", "a$$/10_a"), '10x 10', "adding same name twice; order b-a";
3818+
3819+# Backup files (trailing ~) and hidden files (leading '.') get ignored
3820+mksnip('b', '10_c~', '12~');
3821+mksnip('b', '.10_c', '.12');
3822+is assemble("b$$/10_c", "b$$/10_c~"), '12', "backup file (trailing ~) gets ignored";
3823+is assemble("b$$/10_c", "b$$/.10_c"), '12', "hidden file (leading .) gets ignored";
3824+
3825+# Non-numeric filenames get ignored
3826+mksnip('a', 'foo10_a', 'foo10');
3827+is assemble("b$$/10_c", "a$$/foo10_a"), '12', "file starting with [^ADR0-9] gets ignored";
3828+
3829+# 'R' command replaces existing snippets of same rank
3830+mksnip('a', 'R10_b', '11');
3831+is assemble("a$$/10_a", "b$$/10_c", "a$$/R10_b"), '11', "'R' cmd; replace all";
3832+is assemble("a$$/10_a", "a$$/R10_b", "b$$/10_c"), '11 12', "'R' cmd; replace one (ordered)";
3833+is assemble("b$$/10_c", "a$$/R10_b", "a$$/10_a"), '10 11', "'R' cmd; replace one (reverse order)";
3834+
3835+# 'D' command establishes default that gets overwritten or ignored
3836+mksnip('a', 'D10_a', 'D10');
3837+mksnip('b', 'D10_c', 'D12');
3838+is assemble("a$$/D10_a", "b$$/10_c"), '12', "'D' default; replaced by regular";
3839+is assemble("a$$/D10_a", "b$$/D10_c"), 'D12', "'D' default; replaced by new default (ordered)";
3840+is assemble("b$$/D10_c", "a$$/D10_a"), 'D10', "'D' default; replaced by new default (reverse order)";
3841+is assemble("a$$/D10_a", "a$$/R10_b"), '11', "'D' default; replaced by 'R' cmd";
3842+is assemble("b$$/10_c", "a$$/D10_a"), '12', "'D' default; ignored when regular exists";
3843+
3844+# Ranks are sorted numerically
3845+mksnip('b', '09_a', '09');
3846+mksnip('a', '15_a', '15');
3847+mksnip('b', '2_a', '2');
3848+is assemble("a$$/10_a", "b$$/2_a", "a$$/15_a", "b$$/09_a"), '2 09 10 15', "ranks are sorted numerically";
3849+
3850+# Builtin macros
3851+mksnip('a', '30_a', '_USERNAME_');
3852+mksnip('a', '30_b', '_OUTPUTFILE_');
3853+mksnip('a', '30_c', '_SNIPPETFILE_');
3854+mksnip('a', '30_d', '_HOST_');
3855+is assemble("a$$/30_a"), "$user", "builtin macro _USERNAME_";
3856+is assemble("a$$/30_b"), "out$$", "builtin macro _OUTPUTFILE_";
3857+is assemble("a$$/30_c"), "a$$/30_c", "builtin macro _SNIPPETFILE_";
3858+is assemble("a$$/30_d"), "$host", "builtin macro _HOST_";
3859+
3860+# User macros
3861+mksnip('b', '35_a', 'Line _M1_');
3862+mksnip('b', '35_b', 'Line _M1_ with _M2_');
3863+mksnip('b', '35_c', 'Line _M2_ with _M2_');
3864+is assemble("-m", "_M1_=REP1", "b$$/35_a"), "Line REP1", "single user macro; single occurrence";
3865+is assemble("-m", "_M1_=REP1,_M2_=REP2", "b$$/35_b"), "Line REP1 with REP2", "multiple user macros";
3866+is assemble("-m", "_M2_=REP2", "b$$/35_c"), "Line REP2 with REP2", "single user macro; multiple occurrences";
3867+
3868+# Input pattern
3869+mksnip('a', '10_a.cmd', '10cmd');
3870+is assemble("-i", "\.cmd", "a$$/10_a", "b$$/10_c", "a$$/R10_b", "a$$/10_a.cmd"), '10cmd', "input pattern";
3871+
3872+# Dependency file generation
3873+assemble("-M", "./dep$$", "a$$/10_a", "b$$/10_c");
3874+open(my $fh, '<', "dep$$") or die "can't open dep$$ : $!";
3875+chomp(my @result = <$fh>);
3876+close $fh;
3877+is "$result[0]", "out$$: \\", "dependency file (line 1)";
3878+is "$result[1]", " a$$/10_a \\", "dependency file (line 2)";
3879+is "$result[2]", " b$$/10_c", "dependency file (line 3)";
3880+
3881+rmtree([ "a$$", "b$$", "out$$", "dep$$" ]);

Subscribers

People subscribed via source and target branches