Merge lp:~epics-core/epics-base/rsrvbindiface into lp:~epics-core/epics-base/3.16
- rsrvbindiface
- Merge into 3.16
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 |
Related bugs: |
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.
Commit message
Description of the change
Updates to RSRV, mainly to allow binding to a specific network interfaces via. EPICS_CAS_
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 osiSockDiscover
- 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: osiSockDiscover
BroadcastAddres ses() finds 127.255.255.255 For the default implementation of osiSockDiscover
BroadcastAddres ses()
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 envGetBoolConfi
gParam - 12731. By mdavidsaver
-
libca: addAddrToChanne
lAccessAddressL ist 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 osiSockDiscover
BroadcastAddres ses() - 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
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 <top>/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 <top>/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.<arch> |
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 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, &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->dpvt); |
711 | + prec->dpvt = NULL; |
712 | +} |
713 | + |
714 | +long myAsubRoutine(aSubRecord* prec) { |
715 | + if (!prec->cadr) { |
716 | + /* check types of inputs and outputs */ |
717 | + if (prec->ftva != menuFtypeDOUBLE) |
718 | + return 1; /* oops */ |
719 | + |
720 | + dpvt = malloc(42); |
721 | + prec->cadr = &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 += <list of files to be cleaned> |
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) && EPICS_VERSION_INT < 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=<<i>facility</i>>" 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 & 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 <top>/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 ( ¤t ); |
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(¤t,&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$$" ]); |
Needs compatibility testing