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

Proposed by mdavidsaver
Status: Merged
Merged at revision: 12728
Proposed branch: lp:~epics-core/epics-base/rsrvbindiface
Merge into: lp:~epics-core/epics-base/3.15
Diff against target: 2686 lines (+1058/-707)
34 files modified
documentation/RELEASE_NOTES.html (+29/-0)
src/ca/client/CAref.html (+13/-20)
src/ca/client/addrList.h (+1/-1)
src/ca/client/iocinf.cpp (+6/-5)
src/ca/client/udpiiu.cpp (+13/-0)
src/ca/legacy/pcas/generic/caServerI.cc (+33/-0)
src/ca/legacy/pcas/generic/caServerI.h (+2/-0)
src/ca/legacy/pcas/io/bsdSocket/caServerIO.cc (+17/-0)
src/ca/legacy/pcas/io/bsdSocket/caServerIO.h (+2/-0)
src/ca/legacy/pcas/io/bsdSocket/casDGIntfIO.cc (+18/-59)
src/ioc/db/dbChannel.c (+21/-15)
src/ioc/rsrv/camessage.c (+8/-31)
src/ioc/rsrv/caserverio.c (+1/-1)
src/ioc/rsrv/caservertask.c (+726/-234)
src/ioc/rsrv/cast_server.c (+76/-97)
src/ioc/rsrv/online_notify.c (+15/-197)
src/ioc/rsrv/server.h (+22/-9)
src/libCom/bucketLib/bucketLib.c (+2/-2)
src/libCom/env/envDefs.h (+2/-0)
src/libCom/env/envSubr.c (+14/-1)
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 (+2/-1)
src/libCom/osi/os/cygwin32/osdSock.h (+1/-0)
src/libCom/osi/os/default/osdNetIntf.c (+23/-30)
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/osdSock.h (+1/-0)
src/std/filters/arr.c (+1/-1)
src/std/filters/dbnd.c (+1/-1)
src/std/filters/sync.c (+1/-1)
src/std/filters/ts.c (+1/-1)
To merge this branch: bzr merge lp:~epics-core/epics-base/rsrvbindiface
Reviewer Review Type Date Requested Status
Ralph Lange Needs Fixing
Andrew Johnson Approve
EPICS Core Developers Pending
mdavidsaver Pending
Review via email: mp+282249@code.launchpad.net

This proposal supersedes a proposal from 2015-12-21.

Description of the change

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

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

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

To post a comment you must log in.
Revision history for this message
mdavidsaver (mdavidsaver) wrote : Posted in a previous version of this proposal

Needs compatibility testing

review: Needs Information
Revision history for this message
mdavidsaver (mdavidsaver) wrote :

Rebased against 3.15

Revision history for this message
Andrew Johnson (anj) wrote :

When building for VxWorks 5.5.2 (which we still support):

../udpiiu.cpp: In method `udpiiu::udpiiu(epicsGuard<epicsMutex> &, epicsTimerQueueActive &, epicsMutex &, epicsMutex &, cacContextNotify &, cac &, unsigned int, tsDLList<SearchDest> &)':
../udpiiu.cpp:169: passing `int *' as argument 4 of `setsockopt(int, int, int, char *, int)'

The fix is to cast &flag to (char *) [I tried (void *), but the C++ rules don't allow that].
Later, same target-arch:

../../../src/ioc/rsrv/caservertask.c: In function `tryBind':
../../../src/ioc/rsrv/caservertask.c:135: warning: passing arg 2 of `bind' from incompatible pointer type
../../../src/ioc/rsrv/caservertask.c: In function `rsrv_grap_tcp':
../../../src/ioc/rsrv/caservertask.c:201: parse error before `alen'
../../../src/ioc/rsrv/caservertask.c:202: `alen' undeclared (first use in this function)
../../../src/ioc/rsrv/caservertask.c:202: (Each undeclared identifier is reported only once
../../../src/ioc/rsrv/caservertask.c:202: for each function it appears in.)
../../../src/ioc/rsrv/caservertask.c: In function `rsrv_init':
../../../src/ioc/rsrv/caservertask.c:579: warning: passing arg 4 of `setsockopt' from incompatible pointer type

On line 135 change bind() arg #2 to be (struct sockaddr *) &addr->sa (cast is needed to drop const).
Line 201 is not standard C code, a declaration appears after other statements in block; swapped with preceding line.
Line 579 needs another cast to (char *).

The result builds on all my standard architectures.

Question: Is 'rsrv_grap_tcp' a typo for 'rsrv_grab_tcp'?

Could your point about there being a broadcast route through lo conceivably apply to other OSs too? If so I would prefer that we use some other conditional in the libCom/osi/os/default/osdNetInf.c file to enable that code; adding a port to a new OS should be possible without having to make *any* changes to the existing code. I would define some macro in osi/os/Linux/osdSock.h and use that instead of __linux__ in osdNetInf.c.

More to come, I'll try to do some testing this afternoon.

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

On 01/20/2016 02:10 PM, Andrew Johnson wrote:
> Question: Is 'rsrv_grap_tcp' a typo for 'rsrv_grab_tcp'?

I'm going to go with, yes.

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

On 01/20/2016 02:10 PM, Andrew Johnson wrote:
> Could your point about there being a broadcast route through lo conceivably apply to other OSs too?

I suspect so as it seems like a reasonable thing to do. However, I
couldn't find any reference, and am not in a position to test any other
targets except RTEMS (where it isn't useful).

> ... I would define some macro in osi/os/Linux/osdSock.h and use that instead of __linux__ in osdNetInf.c.

Seems reasonable.

Revision history for this message
Andrew Johnson (anj) wrote :

I'm not quite sure what this means, but on my Windows & Cygwin box:

ECLIPSE$ route PRINT -4 127*
===========================================================================
Interface List
 12...ac 16 2d 0d 54 de ......Intel(R) 82579LM Gigabit Network Connection
  1...........................Software Loopback Interface 1
 13...00 00 00 00 00 00 00 e0 Microsoft ISATAP Adapter
 11...00 00 00 00 00 00 00 e0 Microsoft 6to4 Adapter
===========================================================================

IPv4 Route Table
===========================================================================
Active Routes:
Network Destination Netmask Gateway Interface Metric
        127.0.0.0 255.0.0.0 On-link 127.0.0.1 306
        127.0.0.1 255.255.255.255 On-link 127.0.0.1 306
  127.255.255.255 255.255.255.255 On-link 127.0.0.1 306
===========================================================================
Persistent Routes:
  None

It's likely that Cygwin would see those same routes, and it uses the osi/os/default implementation (how would I test that?). We might want to add similar code to the osi/WIN32/osdNetIntf.c but I don't really understand what that's doing at line 134, so I'm not going to suggest we make any changes to that right now.

Revision history for this message
Andrew Johnson (anj) wrote :
Download full text (3.3 KiB)

Seems to be working for me on VxWorks 6.9, I can connect to PVs, and outgoing CA client connections still work OK too (although I didn't test setting CA_ADDR_LIST).

Also works with a second network interface configured at 10.0.0.2 (but not actually connected to anything):

iocanj> casr 2
Channel Access Server V4.13
No clients connected.
Server interface 0.0.0.0:5064
 UDP receiver 1 0.0.0.0:5064
Beacon destination 10.0.0.255:5065
Beacon destination 164.54.11.255:5065
There are currently 1288 bytes on the server's free list
7 client(s), 0 channel(s), 0 event(s) (monitors) 0 putNotify(s)
0 small buffers (16384 bytes ea), and 0 jumbo buffers (16408 bytes ea)
The server's resource id conversion table:
Bucket entries in use = 0 bytes in use = 16404
Bucket entries/hash id - mean = 0.000000 std dev = 0.000000 max = 0
The server's array size limit is 16408 bytes max
Channel Access Address List
10.0.0.255:5065
164.54.11.255:5065
value = 19 = 0x13

The "Server interface 0.0.0.0:5064" and "UDP receiver 1 0.0.0.0:5064" lines are slightly confusing in the above status message, although I guess they do make sense. At least the casr command now shows the dynamically allocated TCP port number (which used to only show up at iocInit, making it hard to look up later on).

when running on an IOC without DNS properly configured the two entries in the CA Address list at the bottom take a minute or two each to print while waiting for the DNS search to time out; in my experience network administrators don't add names for their broadcast IP addresses to DNS, so why are we querying DNS before printing just the numbers in that list? I guess there might be host IPs in there too, but in this case I would be happy with just printing the numbers even for them. You probably haven't touched this part of the code though...

Pointing EPICS_CAS_INTF_ADDR_LIST to my secondary interface's IP address stopped CA searches from the primary network being seen/replied to.

When I pointed EPICS_CAS_INTF_ADDR_LIST to my primary interface's broadcast address though, the error message was not quite as helpful as it could be:

Starting iocInit
############################################################################
## EPICS R3.15.3-DEV $$Date: Sun 2015-11-22 18:10:40 +0100 $$
## EPICS Base built Jan 20 2016
############################################################################
Warning: RSRV has empty beacon address list
CAS: Socket bind 164.54.11.255:5064 error was "S_errno_EINVAL"
Thread tShell0 (0x6f6c70) can't proceed, suspending.

Users are going to try putting broadcast addresses in EPICS_CAS_INTF_ADDR_LIST (since they're used to using them on the client side), so if we aren't going to recognize and fix that setting automatically, there should be some kind of diagnostic explaining what the problem is in a bit more detail than is shown above. Calling cantProceeed() in that case is probably correct though.

How might I go about testing the multi-cast support?

This branch at least needs an entry in the Release Notes explaining what's new and how to configure the new features. I'm not sure if there's a suitable place in the AppDevGuide for more detail or not, although I think we...

Read more...

review: Approve
Revision history for this message
Ralph Lange (ralph-lange) wrote :

So this branch was overtaking my rebase on GitHub... need to rearrange.

I pushed the fix for the issue with compiling on MinGW to the 3.15 branch (http://bazaar.launchpad.net/~epics-core/epics-base/3.15/revision/12717), so that should be handled.

Do we need more detailed test instructions?
Remember that we wanted to trigger a serious testing campaign for this, so we should have some kind of rough test plan that we want people to run.

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

> Do we need more detailed test instructions?

We do, so I'll write something up.

12737. By Andrew Johnson

Fix build for vxWorks 5.5.2

Revision history for this message
Andrew Johnson (anj) wrote :

Committed fixes for building on 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

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

Fixed incorrect broadcast address list construction. Made the "no addresses" error message longer. Added release notes. Teach PCAS to listen for mcast search requests as well.

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

> ... the two entries in the CA Address list ...

This isn't actually the CA address list, but rather the beacon address list printed a second time. The first uses ipAddrToDottedIP(), and the second is now removed.

FYI the only place I've found the actual CA address list (for the dbCa context) is buried in "dbcar '' 8".

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

> ... How might I go about testing the multi-cast support? ...

Set:

EPICS_CAS_INTF_ADDR_LIST='0.0.0.0 224.0.2.9' EPICS_CA_ADDR_LIST='224.0.2.9' EPICS_CA_AUTO_ADDR_LIST=NO

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

Since this branch allows 127.255.255.255 to be discovered and included in the default address list (for Linux), some configurations will result in more spurious duplicate PV warnings when IOC and client are on the same system. I go back and forth about whether this is acceptable or not. Thoughts?

12744. By Andrew Johnson

Fix build for vxWorks 5.5.2 (again)

Revision history for this message
Andrew Johnson (anj) wrote :

You broke the generation of beacon anomalies at IOC startup somehow.

There is a tool build as part of src/ca called casw which monitors for and displays beacon anomalies, using exactly the same code that the clients use. When I start a regular 3.14 softIoc with casw running I get the expected anomalies displayed, but when starting a softIoc from this branch it shows nothing.

The IOC is sending out beacons at the usual exponentially-decaying rate since I do see them displayed when I ask for every beacon by running 'casw -i 2':

  localhost:5064 2016-01-21 12:56:35.851115672
  tux.aps.anl.gov:5064 2016-01-21 12:56:35.851214480
  localhost:5064 2016-01-21 12:56:35.871209057
  tux.aps.anl.gov:5064 2016-01-21 12:56:35.871263061
  localhost:5064 2016-01-21 12:56:35.911352969
  tux.aps.anl.gov:5064 2016-01-21 12:56:35.911395578
  localhost:5064 2016-01-21 12:56:35.991482300
  tux.aps.anl.gov:5064 2016-01-21 12:56:35.991524021
  localhost:5064 2016-01-21 12:56:36.151682412
  tux.aps.anl.gov:5064 2016-01-21 12:56:36.151731490
  localhost:5064 2016-01-21 12:56:36.471809095
  tux.aps.anl.gov:5064 2016-01-21 12:56:36.471859590
  localhost:5064 2016-01-21 12:56:37.111908997
  tux.aps.anl.gov:5064 2016-01-21 12:56:37.111952211
  localhost:5064 2016-01-21 12:56:38.392053916
  tux.aps.anl.gov:5064 2016-01-21 12:56:38.392075091
  localhost:5064 2016-01-21 12:56:40.952238006
  tux.aps.anl.gov:5064 2016-01-21 12:56:40.952306370
  localhost:5064 2016-01-21 12:56:46.072448531
  tux.aps.anl.gov:5064 2016-01-21 12:56:46.072515535

I tried starting the IOC with EPICS_CAS_AUTO_BEACON_ADDR_LIST=NO EPICS_CAS_BEACON_ADDR_LIST=tux and while the output from 'casr 2' shows only one beacon destination address, I still get no joy with the anomaly detection from casw.

review: Needs Fixing
12745. By mdavidsaver

rsrv: beaconCounter not updated

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

> You broke the generation of beacon anomalies at IOC startup somehow.

Good catch, and now fixed. I had checked that beacons were sent, but didn't look at the contents. The counter wasn't being copied into the packet buffer, so it was always sending a count of 0.

Another (intentional) change is that beacons are sent with the "address" field set to zero, so carepeater will fill it in based on the source address. With the switch to an unconnected socket this gives the correct result (verified with casw and wireshark).

Revision history for this message
Andrew Johnson (anj) wrote :

Beacons fixed, thanks.

Trying multicast name resolution:

tux$ EPICS_CAS_INTF_ADDR_LIST='0.0.0.0 224.0.2.9' EPICS_CA_ADDR_LIST=224.0.2.9 EPICS_CA_AUTO_ADDR_LIST=NO bin/linux-x86_64/softIoc -x anj
Starting iocInit
############################################################################
## EPICS R3.15.3-DEV $$Date: Sun 2015-11-22 18:10:40 +0100 $$
## EPICS Base built Jan 21 2016
############################################################################
CAS address list can not contain 0.0.0.0 and other addresses, ignoring...
iocRun: All initialization complete
epics>

I get the same output from 'casr 2' without the warning message by omitting the 0.0.0.0 interface address, so I'll assume including it was just a mistake in your instructions above.

Searches from the same subnet succeed with EPICS_CA_ADDR_LIST=224.0.2.9 EPICS_CA_AUTO_ADDR_LIST=NO. Do I need to have our network admins configure something for searches to work across subnets? They don't for me at the moment using the same unmodified 3.14-based client. This was only an experimental feature though so it's not a blocker at all.

If I configure the IOC to only use the localhost interface, why does it still send beacons through my primary interface?

tux$ EPICS_CAS_INTF_ADDR_LIST=localhost bin/linux-x86_64/softIoc -x anj
Starting iocInit
############################################################################
## EPICS R3.15.3-DEV $$Date: Sun 2015-11-22 18:10:40 +0100 $$
## EPICS Base built Jan 21 2016
############################################################################
iocRun: All initialization complete
epics> casr 2
Channel Access Server V4.13
No clients connected.
Server interface 127.0.0.1:5064
 UDP receiver 1 127.0.0.1:5064
 UDP receiver 2 127.255.255.255:5064
Beacon destination 127.255.255.255:5065
Beacon destination 164.54.11.255:5065
...

I had expected that explicitly setting the interface would also configure the beacons to be sent through the same interface(s), although I do realize the original code didn't work like that and it can be done manually. I'd be OK with leaving that change for later if there are objections to fixing it here though.

I also have some minor modifications to casr which I'll finish off next week.

review: Approve
Revision history for this message
Ralph Lange (ralph-lange) wrote :

> Trying multicast name resolution:
>
> tux$ EPICS_CAS_INTF_ADDR_LIST='0.0.0.0 224.0.2.9' EPICS_CA_ADDR_LIST=224.0.2.9
> EPICS_CA_AUTO_ADDR_LIST=NO bin/linux-x86_64/softIoc -x anj
> Starting iocInit
> ############################################################################
> ## EPICS R3.15.3-DEV $$Date: Sun 2015-11-22 18:10:40 +0100 $$
> ## EPICS Base built Jan 21 2016
> ############################################################################
> CAS address list can not contain 0.0.0.0 and other addresses, ignoring...
> iocRun: All initialization complete
> epics>
>
> I get the same output from 'casr 2' without the warning message by omitting
> the 0.0.0.0 interface address, so I'll assume including it was just a mistake
> in your instructions above.
>
> Searches from the same subnet succeed with EPICS_CA_ADDR_LIST=224.0.2.9
> EPICS_CA_AUTO_ADDR_LIST=NO.

Hm.
Shouldn't there be a way to specify: "bind to all available network interfaces, plus the extra multicast address".

It look s like the settings from Michael's instructions were trying to do this, but the "0.0.0.0" was rejected. (Which might be wrong.)
Do searches from the same subnet with broadcasts or directed to the IOCs IP work in this configuration?

12746. By mdavidsaver

rsrv: fix wildcard check ordering

filter out mcast addresses before wildcard uniqueness check.

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

Ha, I should be more careful about testing exactly the examples I give.

> EPICS_CAS_INTF_ADDR_LIST='0.0.0.0 224.0.2.9'

Triggered the error.

> EPICS_CAS_INTF_ADDR_LIST='224.0.2.9 0.0.0.0'

Didn't.

Now fixed so that ordering shouldn't matter.

Also, EPICS_CAS_INTF_ADDR_LIST='224.0.2.9' should be equivalent to both of these. If the interface list is empty after mcast destinations are removed, then the wildcard is added.

To actually trigger this error (now cantProceed) include the wildcard address and a specific interface address.

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

> Do I need to have our network admins configure something for searches to work across subnets?

I'm fairly certain that I'm doing the right thing on the socket level. But I just don't know how what additional work is needed at the router/switch level. As I think about it, this is probably a good tech-talk question.

For layer 2 devices (hubs and dumb switches) multicasts and broadcasts are quite similar. In the presence of layer 3 (routers and "smart" switches) things will be different. I'm not sure how to test this situation. Note that how this works, and what configuration is needed, may well depend on which mcast address is used.

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.

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

More changes to beacon address list population (I think I've got it this time).

https://wiki-ext.aps.anl.gov/epics/index.php/RSRV_Configuration_Test_Plan

Details most tested configurations with expected results.

Revision history for this message
Andrew Johnson (anj) wrote :
Download full text (4.0 KiB)

I have a set of changes which significantly tidy up the output from casr. Should I commit them here, or to 3.16 after this branch has been merged up?

Here's some new output:

epics> casr
Channel Access Server V4.13
2 client(s) connected.

epics> casr 1
Channel Access Server V4.13
2 client(s) connected:
    CA-client at 164.54.9.24:57238 'tux.aps.anl.gov':
 User 'anj', V4.13, Priority = 0, 2 Channel(s)
    CA-client at 164.54.8.21:33434 'venus':
 User 'anj', V4.11, Priority = 0, 1 Channel(s)
CAS-TCP server 0.0.0.0:5064
    CAS-UDP name server 0.0.0.0:5064
2 CAS-beacon destinations:
    127.255.255.255:5065
    164.54.11.255:5065

epics> casr 2
Channel Access Server V4.13
2 client(s) connected:
    CA-client at 164.54.9.24:57238 'tux.aps.anl.gov':
 User 'anj', V4.13, Priority = 0, 2 Channel(s)
        Channel: 'anj:exit'
        Channel: 'anj:exit.VAL[0]{"ts":{}}'
    CA-client at 164.54.8.21:33434 'venus':
 User 'anj', V4.11, Priority = 0, 1 Channel(s)
        Channel: 'anj:exit.A'
CAS-TCP server 0.0.0.0:5064
    CAS-UDP name server 0.0.0.0:5064
2 CAS-beacon destinations:
    127.255.255.255:5065
    164.54.11.255:5065

epics> casr 3
Channel Access Server V4.13
2 client(s) connected:
    CA-client at 164.54.9.24:57238 'tux.aps.anl.gov':
 User 'anj', V4.13, Priority = 0, 2 Channel(s)
        Channel: 'anj:exit'
            field_type=DBF_DOUBLE (8 bytes), dbr_type=DBF_DOUBLE, 1 element, no filters
            # on eventq=1, access=rw
        Channel: 'anj:exit.VAL[0]{"ts":{}}'
            field_type=DBF_DOUBLE (8 bytes), dbr_type=DBF_DOUBLE, 1 element
            2 filters (1 pre eventq, 0 post eventq)
            final field_type=DBF_DOUBLE (8B), 1 element
            # on eventq=1, access=rw
    CA-client at 164.54.8.21:33434 'venus':
 User 'anj', V4.11, Priority = 0, 1 Channel(s)
        Channel: 'anj:exit.A'
            field_type=DBF_DOUBLE (8 bytes), dbr_type=DBF_DOUBLE, 1 element, no filters
            # on eventq=1, access=rw
CAS-TCP server 0.0.0.0:5064
    CAS-UDP name server 0.0.0.0:5064
2 CAS-beacon destinations:
    127.255.255.255:5065
    164.54.11.255:5065

epics> casr 4
Channel Access Server V4.13
2 client(s) connected:
    CA-client at 164.54.9.24:57238 'tux.aps.anl.gov':
 User 'anj', V4.13, Priority = 0, 2 Channel(s)
 Task Id = 0x7f1e2c005680, Socket FD = 7
 10.30 secs since last send, 10.30 secs since last receive
 Unprocessed request bytes = 0, Undelivered response bytes = 0
 State = up
        Channel: 'anj:exit'
            field_type=DBF_DOUBLE (8 bytes), dbr_type=DBF_DOUBLE, 1 element, no filters
            # on eventq=1, access=rw
        Channel: 'anj:exit.VAL[0]{"ts":{}}'
            field_type=DBF_DOUBLE (8 bytes), dbr_type=DBF_DOUBLE, 1 element
            2 filters (1 pre eventq, 0 post eventq)
                Array (arr): start=0, incr=1, end=0
                Timestamp (ts)
            final field_type=DBF_DOUBLE (8B), 1 element
            # on eventq=1, access=rw
    CA-client at 164.54.8.21:33434 'venus':
 User 'anj', V4.11, Priority = 0, 1 Channel(s)
 Task Id = 0x7f1e2c005f80, Socket FD = 9
 11.35 secs since last send, 11.35 secs since last receive
 Unprocessed request bytes = 0, Undelivered response bytes...

Read more...

Revision history for this message
Ralph Lange (ralph-lange) wrote :

No strong preference.

But since the changes are probably in one source file and thus easy to separate from the functional changes, why not here.

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

Well I think the answer is simple and none technical. Commit it if you have time to update the wiki ;)

Revision history for this message
Andrew Johnson (anj) wrote :

Ok; the changes are mostly in rsrv/caservertask.c, but I also added more interest-level conditionals to dbChannelShow() and modified printf() strings in all the std/filters channel_report() methods and in libCom's bucketShow(). I will commit and make the changes on the Wiki page to match.

I did notice that casr() no longer prints a log_one_client() report for the UDP (i.e. name-server) clients. There used to be only one such client thread, but now the number can vary, with one or two threads for each interface. I had removed the UDP reporting code from the log_one_client() routine since it was no longer being used, but I just put it back in case we want to add these threads back into the report. I think I know how to do that if it's wanted.

How are the multicast monitors supposed to work? There isn't a separate thread started for them, the code just joins every primary UDP socket to the multicast group(s) configured. Isn't that going to cause multiple search replies from any IOC that has two interfaces say if both receive a copy of the same multicast search request? We might need a way to configure which multicast addresses get joined to which interfaces if this starts to be used much.

12749. By Andrew Johnson

Improve casr() output

Revision history for this message
Andrew Johnson (anj) wrote :

I added 2 comments to the wiki page in bold red, the beacon list for Test #2 seems wrong, and Test #6 gives me different results (the interface on 0.0.0.0 is still open, so clients in the same subnet can still connect without having to use the multicast address).

I think I'm done with this now; apart from multi-cast it builds and works fine.

review: Approve
Revision history for this message
mdavidsaver (mdavidsaver) wrote :

> How are the multicast monitors supposed to work? ... Isn't that going to cause multiple search replies from any IOC that has two interfaces say if both receive a copy of the same multicast search request?

The best I can say is probably not, at least on Linux. I haven't found any clear description, of all that is involved here, so I could be completely wrong. As I understand it, as with any ip packet, a multicast packet is always received on a specific interface, and won't be forwarded absent some specific routing rule ("ip mroute" is empty by default). So an mcast should only be delivered to sockets bound to the interface it was received on, and joined to the group.

Linux might have some additional hoops to jump. I find some references to needing to disable reverse path filtering (rp_filter), though this isn't enabled by default w/ debian 8.

Once the test-plan wiki page is deemed correct, and the code to match, we had discussed putting out a request for testing on tech-talk. I'd like to have this tested in a more realistic setting.

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

> the beacon list for Test #2 seems wrong

Not surprising. For some reason I'm getting confused about whether this should match the server or client interface address list. I've "fixed" this at least twice now and two parity flips means I need some outside input. Can you fix the wiki page to what you think it should be?

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

A given socket will only send multicast packets out a single interface (as determined by the local routing table or IP_MULTICAST_IF). So no duplication there.

There probably should be some way of configuring IP_MULTICAST_TTL though. Would you care to pick a EPICS_CA_* name for this Andrew?

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

> Test #6 gives me different results ...

... Due to the way my test system is configured :P You result is the correct one.

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

> ... We might need a way to configure which multicast addresses get joined to which interfaces if this starts to be used much.

> ... as determined by the local routing table or IP_MULTICAST_IF ...

Yes, to allow full flexibility the both client and server address lists would need to be configured with pairs of multicast+interface address. However, I'm inclined to forgo this unnecessary until someone describes a situation where this is necessary, or even helpful.

Revision history for this message
Andrew Johnson (anj) wrote :
Download full text (3.4 KiB)

Beacons should be related to the server interfaces in use. In the past they weren't, originally because there were no EPICS_CAS_ variables, although later I think Jeff just re-used the contents of EPICS_CA_ADDR_LIST for rsrv's beacon list unless you explicitly set EPICS_CAS_BEACON_ADDR_LIST. I don't think the code is quite right yet, although it seems fairly close.

Starting a softIoc with all defaults, I get this:
tux$ bin/linux-x86_64/softIoc -x anj
...
CAS-TCP server on 0.0.0.0:5064 with
    CAS-UDP name server on 0.0.0.0:5064
Sending CAS-beacons to 2 addresses:
    127.255.255.255:5065
    164.54.11.255:5065

If I set EPICS_CAS_INTF_ADDR_LIST to my primary IP address, I get this:
tux$ EPICS_CAS_INTF_ADDR_LIST='tux' bin/linux-x86_64/softIoc -x anj
...
CAS-TCP server on 164.54.9.24:5064 with
    CAS-UDP unicast name server on 164.54.9.24:5064
    CAS-UDP broadcast name server on 164.54.11.255:5064
Sending CAS-beacons to 1 address:
    164.54.11.255:5065

Setting it to localhost I get this:
tux$ EPICS_CAS_INTF_ADDR_LIST='localhost' bin/linux-x86_64/softIoc -x anj
...
CAS-TCP server on 127.0.0.1:5064 with
    CAS-UDP unicast name server on 127.0.0.1:5064
    CAS-UDP broadcast name server on 127.255.255.255:5064
Sending CAS-beacons to 1 address:
    127.255.255.255:5065

I can add addresses to the beacon list:
tux$ EPICS_CAS_INTF_ADDR_LIST='localhost' EPICS_CAS_BEACON_ADDR_LIST=224.0.2.9 bin/linux-x86_64/softIoc -x anj
...
CAS-TCP server on 127.0.0.1:5064 with
    CAS-UDP unicast name server on 127.0.0.1:5064
    CAS-UDP broadcast name server on 127.255.255.255:5064
Sending CAS-beacons to 2 addresses:
    127.255.255.255:5065
    224.0.2.9:5065

Those all look correct to me so far, beacons are being sent to the broadcast addresses of the interfaces that my servers are listening on, plus any others I explicitly specify. However if I set EPICS_CA_AUTO_ADDR_LIST=NO the interface list gets ignored for beacons and I get the 'empty beacon address list' warning:
tux$ EPICS_CAS_INTF_ADDR_LIST='localhost' EPICS_CA_AUTO_ADDR_LIST=NO bin/linux-x86_64/softIoc -x anj
...
Warning: RSRV has empty beacon address list
...
CAS-TCP server on 127.0.0.1:5064 with
    CAS-UDP unicast name server on 127.0.0.1:5064
    CAS-UDP broadcast name server on 127.255.255.255:5064
Sending CAS-beacons to 0 addresses:

In this case it still seems to be copying the initial beacon list from the EPICS_CA_ADDR_LIST instead of the interface list because I can put addresses in the beacon list by adding them to EPICS_CA_ADDR_LIST, e.g:
tux$ EPICS_CAS_INTF_ADDR_LIST='localhost' EPICS_CA_ADDR_LIST=224.0.2.9 EPICS_CA_AUTO_ADDR_LIST=NO \
bin/linux-x86_64/softIoc -x anj
...
CAS-TCP server on 127.0.0.1:5064 with
    CAS-UDP unicast name server on 127.0.0.1:5064
    CAS-UDP broadcast name server on 127.255.255.255:5064
Sending CAS-beacons to 1 address:
    224.0.2.9:5065

A different issue: If I put a multicast address in EPICS_CAS_BEACON_ADDR_LIST without explicitly setting EPICS_CAS_INTF_ADDR_LIST, casr reports that the server is monitoring that address, which doesn't seem right. It didn't do that when I put it in EPICS_CA_ADDR_LIST (above):
tux$ EPICS_CAS_BEACON_ADDR_LIST=224.0.2.9 bin/l...

Read more...

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.

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

> Beacons should be related to the server interfaces in use.

Ok, auto beacon list is no longer tied to client auto address list.

Also, the server interface list will no longer use the beacon address list as a fallback.

Revision history for this message
Andrew Johnson (anj) wrote :

Thanks, I think this makes much more sense. The behavior change does need documenting though, in the Release Notes entry and probably in CAref.html as well. I can update the Wiki page to match if you like.

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

> The behavior change does need documenting though, in the Release Notes entry and probably in CAref.html

Which raises the question: what about PCAS?

> I can update the Wiki page to match if you like.

Please do. And you may assume in future that I would always like.

12751. By mdavidsaver

pcas: no fallbacks for beacon address list

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

12752. By mdavidsaver

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

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

12753. By mdavidsaver

update release notes

12754. By mdavidsaver

rsrv: EPICS_CAS_IGNORE_ADDR_LIST

12755. By mdavidsaver

libCom: fix debug print formats in osiSockDiscoverBroadcastAddresses()

12756. By mdavidsaver

rsrv: "casr 1" shows ignore list

12757. By mdavidsaver

update release notes

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

> Beacons should be related to the server interfaces in use.

I made the corresponding change to PCAS as well.

I also made an attempt to teach RSRV about EPICS_CAS_IGNORE_ADDR_LIST, which I think matches PCAS wrt. configuration options.

Updated CAref.html and release notes.

Revision history for this message
Andrew Johnson (anj) wrote :

Unfortunately I think your PCAS changes have broken something. I can run the base-3.15 excas example with no arguments and I get no errors, but if I run it from this branch I get the following errors, printed at the exponentially-slowing beacon generation intervals:

../../../../../../src/ca/legacy/pcas/io/bsdSocket/casDGIntfIO.cc: CA beacon routing (connect to "164.54.11.255:5065") error was "Invalid argument"
../../../../../../src/ca/legacy/pcas/io/bsdSocket/casDGIntfIO.cc: CA beacon routing (connect to "164.54.11.255:5065") error was "Invalid argument"
../../../../../../src/ca/legacy/pcas/io/bsdSocket/casDGIntfIO.cc: CA beacon routing (connect to "164.54.11.255:5065") error was "Invalid argument"

The IP address in that error message is the broadcast address of my local subnet. Connections to the excas PVs from a local client do succeed, so I think it is listening on the primary interface properly. Running casw while starting the server shows beacons coming from localhost but not from the machine's regular IP address (the opposite of running the base-3.15 version of excas).

I updated the Wiki page to remove my last red question and fix the expected casr output there, and fixed the CAS-beacon list in Test #6.

About the wiki page, I don't think we really need to set EPICS_CA_ADDR_LIST or AUTO_ADDR_LIST when starting the IOC in most of these tests, dbCa will always connect to local channels through the internal channel provider anyway, it doesn't use the network at all. Test #5 and maybe #6 could keep those settings though.

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

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

Ok, I think this is fixed as well. PCAS was sending beacons from a connect()d UDP socket as RSRV was. Now changed to use sendto() and leave the origin interface field zero'd, as RSRV now does.

Revision history for this message
Andrew Johnson (anj) wrote :

CAS beacon issue fixed, will merge after Ralph's approval.

review: Approve
12760. By Ralph Lange

pcas: fix compilation on MSC Windows

Revision history for this message
Ralph Lange (ralph-lange) wrote :

On Windows, I am not getting any of the thread's messages, type

CAS-TCP server on 10.5.1.1:5064 with
    CAS-UDP unicast name server on 10.5.1.1:5064
    CAS-UDP broadcast name server on 10.5.1.255:5064

Have these messages been removed again?

Revision history for this message
Ralph Lange (ralph-lange) wrote :

More testing:

cygwin does show the messages.
MSVC 12.0 (both 64 and 32 bit) and MinGW32 do not.

Revision history for this message
Ralph Lange (ralph-lange) wrote :

Rough test for the PCAS changes

On 64bit Linux:
The CA Gateway (trunk) compiles, the (very few) unit tests succeed.
However, beacons and reconnection behavior are not tested by these tests yet.

12761. By Andrew Johnson

Report UDP client status on Windows

Revision history for this message
Andrew Johnson (anj) wrote :

Ah yes, the code for casr() does use ifdef _WIN32 and doesn't call log_one_client() from that section of code. I just committed a fix.

Revision history for this message
Ralph Lange (ralph-lange) wrote :

Sorry if I wasn't clear enough.

The Windows builds show *no* thread related messages, none at all.

[...]
iocRun: All initialization complete
epics> casr 1
Channel Access Server V4.13
No clients connected.
Sending CAS-beacons to 2 addresses:
    172.21.63.255:5065
    192.168.56.255:5065
epics> casr 2
Channel Access Server V4.13
No clients connected.
Sending CAS-beacons to 2 addresses:
    172.21.63.255:5065
    192.168.56.255:5065
epics> casr 3
Channel Access Server V4.13
No clients connected.
Sending CAS-beacons to 2 addresses:
    172.21.63.255:5065
    192.168.56.255:5065
epics>

12762. By Ralph Lange

rsrv: populate the server list - also on Windows builds

Revision history for this message
Ralph Lange (ralph-lange) wrote :

Bummer: the server list was empty (not being populated) on Windows.
I just committed a fix.

Revision history for this message
Ralph Lange (ralph-lange) wrote :
Download full text (4.4 KiB)

I did a complete test on my windows box, report is as follows.

The cygwin target needs fixing. I'm not sure what's happening there.

Please also add tests on the behavior when another IOC is already running.
It is not covered by the test plan, but I have seen IOCs hanging or exiting.

These tests are pretty tedious. Excellent candidate for automation...

---- snip ----
Test Report rsrvbindiface on Windows 7

Test Plan https://wiki-ext.aps.anl.gov/epics/index.php/RSRV_Configuration_Test_Plan
Version 2016-02-16T14:44

Tested 2016-02-25T09:00 by Ralph
                duration 2 hrs
Version lp:~epics-core/epics-base/rsrvbindiface revision 12762
                    plus commit 12716..12717 from lp:~epics-core/epics-base/3.15
Targets MSVC 12.0 (amd64), MSVC (x86), Cygwin (x86_64), MinGW32 (x86)
                dynamic builds
Machine Latitude E7240 (intel i3) running Windows 7

Test #1 Defaults
                            MSVC 64 MSVC 32 Cygwin MinGW32
    client get pass pass pass pass
    epicsParamShow pass pass pass pass
    casr 1 pass pass pass pass
    dbcar "" 8 pass pass pass pass

Note on Cygwin:
- warning at boot time
    Warning: Duplicate EPICS CA Address list entry "169.254.255.255:5065" discarded
- casr and dbcar (UDP search destination) show this interface address (link-local)
  in addition to the two regular interfaces

Test #2 Client address list only
                            MSVC 64 MSVC 32 Cygwin MinGW32
    client get pass pass pass pass
    epicsParamShow pass pass pass pass
    casr 1 pass pass pass pass
    dbcar "" 8 pass pass pass pass

Note on Cygwin:
- warning at boot time
    Warning: Duplicate EPICS CA Address list entry "169.254.255.255:5065" discarded
- casr shows this interface address in addition to the two regular interfaces

Test #3 Client and Server on a single interface
                            MSVC 64 MSVC 32 Cygwin MinGW32
    client get pass pass FAIL pass
    epicsParamShow pass pass FAIL pass
    casr 1 pass pass FAIL pass
    dbcar "" 8 pass pass FAIL pass

Failure on Cygwin:
- error at boot time
    CAS: UDP Socket bcast bind error: "Cannot assign requested address"
  IOC hangs after that message

Test #4 Client and Server on multiple interfaces
                            MSVC 64 MSVC 32 Cygwin MinGW32
    client get pass pass FAIL pass
    epicsParamShow pass pass FAIL pass
    casr 1 pass pass FAIL pass
    dbcar "" 8 pass pass FAIL pass

Failure on Cygwin:
- error at boot time
    CAS: UDP Socket bcast bind error: "Cannot assign requested address"
  IOC hangs ...

Read more...

review: Needs Fixing
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

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

> Please also add tests on the behavior when another IOC is already running.

Good point. Provisionally I think the instruction would be to repeat all tests with a second IOC running w/ default config. Just fixed an mistake here were I used 'errno' instead of 'SOCKERRNO'.

> CAS: UDP Socket bcast bind error: "Cannot assign requested address"

This is a failure to bind a UDP socket to an interface broadcast address. So it seems likely that cygwin is (unavoidably) letting the winsock behavior show through. In caservertask.c, I'vee replaced the three '!defined(_WIN32)' with '!(defined(_WIN32) || defined(__CYGWIN32__))'.

> IPv4 Multicast name lookup not supported by this target

The direct cause is that the macro IP_ADD_MEMBERSHIP is not defined in caservertask.c, which is the test I picked for OS support of multicast. I've added an include of "ws2tcpip.h" to WIN32/osdSock.h, which I think is reasonable.

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

> These tests are pretty tedious. Excellent candidate for automation...

Agreed, just not sure how given the requirement for superuser, and the OS differences in network configuration. How can the effect of linux tap interfaces be achieved on windows/macos? What sort of virtual NIC should be used?

Revision history for this message
Eric Norum (wenorum) wrote :

Tuntap is available as an add-on for OS X — I use it with a Verilog simulator to test the ethernet-in-firmware code produced here by Larry Doolittle.

> On Feb 25, 2016, at 8:42 AM, mdavidsaver <email address hidden> wrote:
>
> How can the effect of linux tap interfaces be achieved on windows/macos?

--
Eric Norum
<email address hidden>

Revision history for this message
Andrew Johnson (anj) wrote :

I was doing manual testing here using a combination of localhost and my workstation's primary IP address (i.e. `hostname`); would using just those two be sufficient for automated testing? That would avoid the need for running as root, and I suspect might work on all OSs.

Revision history for this message
Ralph Lange (ralph-lange) wrote :

Testing using a VM is fine, and interfaces are a dime a dozen on those.

Revision history for this message
Andrew Johnson (anj) wrote :

Ok, but requiring that does preclude automated testing on Jenkins (the APS one at least).

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

> would using just those two be sufficient for automated testing?

The test process as currently written wants two interfaces which would be found with EPICS_CA_AUTO_ADDR_LIST=YES. Since the loopback will now be included in this list, I think this would be sufficient.

> at would avoid the need for running as root

On reflection I was making an implicit assumption that the test code would have to change the network configuration, which isn't strictly required. Rather it could inspect, and SKIP if the host doesn't meet some requirements.

Revision history for this message
Andrew Johnson (anj) wrote :

Are you working on some automated tests for this branch, or should I merge it?

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

I have some ideas, but haven't started writing. Go ahead and merge.

Revision history for this message
Ralph Lange (ralph-lange) wrote :
Download full text (4.6 KiB)

I did a partial test.

- The multicast issue seems to be fixed.
- Cygwin still fails miserably

Test results as follows

---- snip ----

Test Report rsrvbindiface on Windows 7 - ONLY PARTIAL TEST

Test Plan https://wiki-ext.aps.anl.gov/epics/index.php/RSRV_Configuration_Test_Plan
Version 2016-02-16T14:44

Tested 2016-02-26T20:00 by Ralph
                duration 1.5 hrs
Version lp:~epics-core/epics-base/rsrvbindiface revision 12765
                    plus commit 12716..12717 from lp:~epics-core/epics-base/3.15
Targets MSVC 12.0 (amd64), MSVC (x86), Cygwin (x86_64), MinGW32 (x86)
                dynamic builds
Machine Latitude E7240 (intel i3) running Windows 7

Test #1 Defaults
                            MSVC 64 MSVC 32 Cygwin MinGW32
    starting second IOC pass pass pass pass
    client get pass pass pass pass
    epicsParamShow pass pass pass pass
    casr 1 pass pass pass pass
    dbcar "" 8 pass pass pass pass

Note on Cygwin:
- warning at boot time
    Warning: Duplicate EPICS CA Address list entry "169.254.255.255:5065" discarded
- casr and dbcar (UDP search destination) show this interface address (link-local)
  in addition to the two regular interfaces

Test #2 Client address list only
                            MSVC 64 MSVC 32 Cygwin MinGW32
    starting second IOC pass pass pass pass
    client get pass pass pass pass
    epicsParamShow pass pass pass pass
    casr 1 pass pass pass pass
    dbcar "" 8 pass pass pass pass

Note on Cygwin:
- warning at boot time
    Warning: Duplicate EPICS CA Address list entry "169.254.255.255:5065" discarded
- casr shows this interface address in addition to the two regular interfaces

Test #3 Client and Server on a single interface
                            MSVC 64 MSVC 32 Cygwin MinGW32
    starting second IOC pass pass FAIL pass
    client get pass pass FAIL pass
    epicsParamShow pass pass FAIL pass
    casr 1 pass pass FAIL pass
    dbcar "" 8 pass pass FAIL pass

Failure on Cygwin:

- error at boot time
    [cas warnings in case of second IOC]
    CAS: UDP Socket bcast bind error: "Cannot assign requested address"
  IOC hangs after that message

Test #4 Client and Server on multiple interfaces
                            MSVC 64 MSVC 32 Cygwin MinGW32
    starting second IOC skip skip FAIL skip
    client get skip skip FAIL skip
    epicsParamShow skip skip FAIL skip
    casr 1 skip skip FAIL skip
    dbcar "" 8 skip skip FAIL skip

Failure on Cygwin:
-...

Read more...

review: Needs Fixing
Revision history for this message
Ralph Lange (ralph-lange) wrote :

Re: Automation

I would not test this as part of a unit test, but move it into a separate job.
In that case it can be executed on a selected (group of) builder(s), which can easily be configured to be the VM kind with two interfaces.

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

> #if !(defined(_WIN32) || defined(__CYGWIN32__))

So clearly __CYGWIN32__ is not the right macro to test. Can you try some others (maybe __CYGWIN__) and/or send me the output of:

> touch empty.c
> cpp -dM empty.c

Revision history for this message
Andrew Johnson (anj) wrote :

> So clearly __CYGWIN32__ is not the right macro to test.

We define our own flags for most architectures, please look for and use those instead of trying to guess what the compiler might define:

tux% cd configure/os
tux% grep OP_SYS_CPPFLAGS *
CONFIG.Common.cygwin-x86:OP_SYS_CPPFLAGS += -DCYGWIN32
CONFIG.Common.freebsdCommon:OP_SYS_CPPFLAGS += -D_BSD_SOURCE
CONFIG.Common.freebsdCommon:OP_SYS_CPPFLAGS += -Dfreebsd
CONFIG.Common.linuxCommon:OP_SYS_CPPFLAGS += -Dlinux
CONFIG.Common.RTEMS: $(USR_CPPFLAGS) $(CMD_CPPFLAGS) $(ARCH_DEP_CPPFLAGS) $(OP_SYS_CPPFLAGS)\
CONFIG.Common.solaris-sparc:OP_SYS_CPPFLAGS += -DSOLARIS=$(SOLARIS_VERSION) $(COMPILER_CPPFLAGS)
CONFIG.Common.solaris-x86:OP_SYS_CPPFLAGS += -DSOLARIS=$(SOLARIS_VERSION) $(COMPILER_CPPFLAGS)
CONFIG.Common.UnixCommon:OP_SYS_CPPFLAGS += -DUNIX
CONFIG.Common.vxWorksCommon:OP_SYS_CPPFLAGS += -DvxWorks=vxWorks
CONFIG.Common.win32-x86-mingw:OP_SYS_CPPFLAGS = -D_MINGW
CONFIG.darwinCommon.darwinCommon:OP_SYS_CPPFLAGS += -Ddarwin

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

I'm not guessing Andrew, I copied shareLib.h. Should I change this to 'CYGWIN32' as well?

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

There is also https://sourceforge.net/p/predef/wiki/OperatingSystems/ which lists '__CYGWIN__'.

Revision history for this message
Andrew Johnson (anj) wrote :

This might explain some strange Cygwin DLL build issues in the past.

I am changing all the Cygwin macro tests to use __CYGWIN__, and will add a comment deprecating our CYGWIN32 macro.

Ah, if I run "cpp -dM -m32 empty.c" it defines __CYGWIN32__ as well as __CYGWIN__ so they evidently removed that when they added 64-bit support. There is no __CYGWIN64__ macro.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'documentation/RELEASE_NOTES.html'
2--- documentation/RELEASE_NOTES.html 2016-02-23 21:43:26 +0000
3+++ documentation/RELEASE_NOTES.html 2016-02-25 16:34:00 +0000
4@@ -43,6 +43,35 @@
5
6 <h2 align="center">Changes between 3.15.2 and 3.15.3</h2>
7
8+<h3>CA server configuration changes</h3>
9+
10+<p>RSRV now honors EPICS_CAS_INTF_ADDR_LIST and binds to only
11+the provided list of network interfaces.
12+Name searches (UDP and TCP) on other network interfaces are ignored.
13+For example on a computer with interfaces 10.5.1.1/24, 10.5.2.1/24, and 10.5.3.1/24,
14+setting "EPICS_CAS_INTF_ADDR_LIST='10.5.1.1 10.5.2.1'" will accept traffic
15+on the .1.1 and .2.1, but ignore from .3.1</p>
16+
17+<p>RSRV now honors EPICS_CAS_IGNORE_ADDR_LIST and ignores
18+UDP messages received from addresses in this list.</p>
19+
20+<p>Previously, CA servers (RSRV and PCAS) would build the beacon address list
21+using EPICS_CA_ADDR_LIST if EPICS_CAS_BEACON_ADDR_LIST was no set.
22+This is no longer done.
23+Sites depending on this should set both envronment variables to the same value.</p>
24+
25+<h3>IPv4 multicast for name search and beacons</h3>
26+
27+<p>libca, RSRV, and PCAS may now use IPv4 multicasting for UDP traffic (name search and beacons).
28+This is disabled by default.
29+To enable multicast address(s) must be listed in EPICS_CA_ADDR_LIST for clients
30+and EPICS_CAS_INTF_ADDR_LIST for servers (IOCs should set both).
31+For example: "EPICS_CAS_INTF_ADDR_LIST='224.0.2.9' EPICS_CA_ADDR_LIST=224.0.2.9".
32+</p>
33+
34+<p>Please note that no IPv4 multicast address is officially assigned for Channel Access by IANA.
35+The example 224.0.2.9 is taken from the AD-HOC Block I range.<p>
36+
37 <h3>Make the NTP Time provider optional on VxWorks</h3>
38
39 <p>Recent versions of VxWorks (sometime after VxWorks 6) provide facilities for
40
41=== modified file 'src/ca/client/CAref.html'
42--- src/ca/client/CAref.html 2015-07-13 18:05:33 +0000
43+++ src/ca/client/CAref.html 2016-02-25 16:34:00 +0000
44@@ -841,23 +841,18 @@
45 Interval</a>.</p>
46
47 <p>CA servers build a list of addresses to send beacons to during
48-initialization. If EPICS_CAS_AUTO_BEACON_ADDR_LIST has the value "YES" then the
49-beacon address list will be automatically configured to contain the broadcast
50-addresses of all LAN interfaces found in the host and the destination address
51-of all point-to-point interfaces found in the host. However, if the user also
52+initialization. If EPICS_CAS_AUTO_BEACON_ADDR_LIST has the value "YES"
53+(the default) this list will be automatically populated with the broadcast
54+addresses of all network interfaces. However, if the user also
55 defines EPICS_CAS_INTF_ADDR_LIST then beacon address list automatic
56 configuration is constrained to the network interfaces specified therein, and
57-therefore only the broadcast addresses of the specified LAN interfaces, and the
58-destination addresses of all specified point-to-point links, will be
59+therefore only the broadcast addresses of the specified LAN interfaces, will be
60 automatically configured.</p>
61
62 <p>If EPICS_CAS_BEACON_ADDR_LIST is defined then its contents will be used to
63 augment any automatic configuration of the beacon address list. Individual
64 entries in EPICS_CAS_BEACON_ADDR_LIST may override the destination port number
65-if ":nnn" follows the host name or IP address there. Alternatively, when both
66-EPICS_CAS_BEACON_ADDR_LIST and EPICS_CAS_INTF_ADDR_LIST are not defined then
67-the contents of EPICS_CA_ADDR_LIST is used to augment the list. Otherwise, the
68-list is not augmented.</p>
69+if ":nnn" follows the host name or IP address there.</p>
70
71 <p>The EPICS_CAS_BEACON_PORT parameter specifies the destination port for
72 server beacons. The only exception to this occurs when ports are specified in
73@@ -868,20 +863,18 @@
74 <h4>Binding a Server to a Limited Set of Network Interfaces</h4>
75
76 <p>The parameter EPICS_CAS_INTF_ADDR_LIST allows a ca server to bind itself to,
77-and therefore accept messages only over, a limited set of the local host's
78+and therefore accept messages received by, a limited set of the local host's
79 network interfaces (each specified by its IP address). On UNIX systems type
80-"netstat -i" (type "ipconfig" on windows) to see a list of the local host's
81-network interfaces. Specifically, UDP search messages addressed to both the IP
82-addresses in EPICS_CAS_INTF_ADDR_LIST and also to the broadcast addresses of
83-the corresponding LAN interfaces will be accepted by the server. By default,
84+"netstat -ie" (type "ipconfig" on windows) to see a list of the local host's
85+network interfaces. By default,
86 the CA server is accessible from all network interfaces configured into its
87 host.</p>
88
89-<p>In R3.14 and previous releases the CA server employed by iocCore did not
90-implement the EPICS_CAS_INTF_ADDR_LIST feature. In this release the iocCore
91-server will read the first IP address from the parameter variable and use that
92-to select which interface to bind to. Any additional IP addresses will be
93-ignored and a warning message displayed during IOC initialization.</p>
94+<p>Until R3.15.4 the CA server employed by iocCore did not
95+implement the EPICS_CAS_INTF_ADDR_LIST feature.</p>
96+
97+<p>Prior to R3.15.4 CA servers would build the beacon address list
98+using EPICS_CA_ADDR_LIST if EPICS_CAS_BEACON_ADDR_LIST was no set.</p>
99
100 <h4>Ignoring Process Variable Name Resolution Requests From Certain Hosts</h4>
101
102
103=== modified file 'src/ca/client/addrList.h'
104--- src/ca/client/addrList.h 2008-09-19 23:27:52 +0000
105+++ src/ca/client/addrList.h 2016-02-25 16:34:00 +0000
106@@ -22,7 +22,7 @@
107 epicsShareFunc void epicsShareAPI configureChannelAccessAddressList
108 ( struct ELLLIST *pList, SOCKET sock, unsigned short port );
109
110-epicsShareFunc void epicsShareAPI addAddrToChannelAccessAddressList
111+epicsShareFunc int epicsShareAPI addAddrToChannelAccessAddressList
112 ( struct ELLLIST *pList, const ENV_PARAM *pEnv,
113 unsigned short port, int ignoreNonDefaultPort );
114
115
116=== modified file 'src/ca/client/iocinf.cpp'
117--- src/ca/client/iocinf.cpp 2013-12-17 18:54:04 +0000
118+++ src/ca/client/iocinf.cpp 2016-02-25 16:34:00 +0000
119@@ -73,7 +73,7 @@
120 /*
121 * addAddrToChannelAccessAddressList ()
122 */
123-extern "C" void epicsShareAPI addAddrToChannelAccessAddressList
124+extern "C" int epicsShareAPI addAddrToChannelAccessAddressList
125 ( ELLLIST *pList, const ENV_PARAM *pEnv,
126 unsigned short port, int ignoreNonDefaultPort )
127 {
128@@ -82,11 +82,11 @@
129 const char *pToken;
130 struct sockaddr_in addr;
131 char buf[32u]; /* large enough to hold an IP address */
132- int status;
133+ int status, ret = -1;
134
135 pStr = envGetConfigParamPtr (pEnv);
136 if (!pStr) {
137- return;
138+ return ret;
139 }
140
141 while ( ( pToken = getToken (&pStr, buf, sizeof (buf) ) ) ) {
142@@ -104,7 +104,7 @@
143 pNewNode = (osiSockAddrNode *) calloc (1, sizeof(*pNewNode));
144 if (pNewNode==NULL) {
145 fprintf ( stderr, "addAddrToChannelAccessAddressList(): no memory available for configuration\n");
146- return;
147+ break;
148 }
149
150 pNewNode->addr.ia = addr;
151@@ -113,9 +113,10 @@
152 * LOCK applied externally
153 */
154 ellAdd (pList, &pNewNode->node);
155+ ret = 0; /* success if anything is added to the list */
156 }
157
158- return;
159+ return ret;
160 }
161
162 /*
163
164=== modified file 'src/ca/client/udpiiu.cpp'
165--- src/ca/client/udpiiu.cpp 2013-06-07 23:08:38 +0000
166+++ src/ca/client/udpiiu.cpp 2016-02-25 16:34:00 +0000
167@@ -163,6 +163,19 @@
168 throwWithLocation ( noSocket () );
169 }
170
171+#ifdef IP_ADD_MEMBERSHIP
172+ {
173+ int flag = 1;
174+ if ( setsockopt ( this->sock, IPPROTO_IP, IP_MULTICAST_LOOP,
175+ (char *) &flag, sizeof ( flag ) ) == -1 ) {
176+ char sockErrBuf[64];
177+ epicsSocketConvertErrnoToString (
178+ sockErrBuf, sizeof ( sockErrBuf ) );
179+ errlogPrintf("CAC: failed to set mcast loopback\n");
180+ }
181+ }
182+#endif
183+
184 int boolValue = true;
185 int status = setsockopt ( this->sock, SOL_SOCKET, SO_BROADCAST,
186 (char *) &boolValue, sizeof ( boolValue ) );
187
188=== modified file 'src/ca/legacy/pcas/generic/caServerI.cc'
189--- src/ca/legacy/pcas/generic/caServerI.cc 2012-11-30 23:42:47 +0000
190+++ src/ca/legacy/pcas/generic/caServerI.cc 2016-02-25 16:34:00 +0000
191@@ -141,6 +141,39 @@
192 return S_cas_success;
193 }
194
195+void caServerI::addMCast(const osiSockAddr& addr)
196+{
197+#ifdef IP_ADD_MEMBERSHIP
198+ epicsGuard < epicsMutex > locker ( this->mutex );
199+ tsDLIter < casIntfOS > iter = this->intfList.firstIter ();
200+ while ( iter.valid () ) {
201+ struct ip_mreq mreq;
202+
203+ memset(&mreq, 0, sizeof(mreq));
204+ mreq.imr_interface = iter->serverAddress().getSockIP().sin_addr;
205+ mreq.imr_multiaddr = addr.ia.sin_addr;
206+
207+ if ( setsockopt ( iter->casDGIntfIO::getFD (), IPPROTO_IP,
208+ IP_ADD_MEMBERSHIP, (char *) &mreq,
209+ sizeof ( mreq ) ) < 0) {
210+ struct sockaddr_in temp;
211+ char name[40];
212+ char sockErrBuf[64];
213+ temp.sin_family = AF_INET;
214+ temp.sin_addr = mreq.imr_multiaddr;
215+ temp.sin_port = addr.ia.sin_port;
216+ epicsSocketConvertErrnoToString (
217+ sockErrBuf, sizeof ( sockErrBuf ) );
218+ ipAddrToDottedIP (&temp, name, sizeof(name));
219+ fprintf(stderr, "CAS: Socket mcast join %s failed with \"%s\"\n",
220+ name, sockErrBuf );
221+ }
222+
223+ iter++;
224+ }
225+#endif
226+}
227+
228 void caServerI::sendBeacon ( ca_uint32_t beaconNo )
229 {
230 epicsGuard < epicsMutex > locker ( this->mutex );
231
232=== modified file 'src/ca/legacy/pcas/generic/caServerI.h'
233--- src/ca/legacy/pcas/generic/caServerI.h 2012-11-30 23:42:47 +0000
234+++ src/ca/legacy/pcas/generic/caServerI.h 2016-02-25 16:34:00 +0000
235@@ -103,6 +103,8 @@
236 caStatus attachInterface ( const caNetAddr & addr, bool autoBeaconAddr,
237 bool addConfigAddr );
238
239+ virtual void addMCast(const osiSockAddr&);
240+
241 void sendBeacon ( ca_uint32_t beaconNo );
242
243 caServerI ( const caServerI & );
244
245=== modified file 'src/ca/legacy/pcas/io/bsdSocket/caServerIO.cc'
246--- src/ca/legacy/pcas/io/bsdSocket/caServerIO.cc 2012-04-12 16:13:50 +0000
247+++ src/ca/legacy/pcas/io/bsdSocket/caServerIO.cc 2016-02-25 16:34:00 +0000
248@@ -14,6 +14,7 @@
249 //
250
251 #include <ctype.h>
252+#include <list>
253
254 #include "epicsSignal.h"
255 #include "envDefs.h"
256@@ -92,6 +93,9 @@
257 autoBeaconAddr = true;
258 }
259
260+ typedef std::list<osiSockAddr> mcastAddrs_t;
261+ mcastAddrs_t mcastAddrs;
262+
263 //
264 // bind to the the interfaces specified - otherwise wildcard
265 // with INADDR_ANY and allow clients to attach from any interface
266@@ -114,6 +118,15 @@
267 pToken);
268 continue;
269 }
270+
271+ epicsUInt32 top = ntohl(saddr.sin_addr.s_addr)>>24;
272+ if (saddr.sin_family==AF_INET && top>=224 && top<=239) {
273+ osiSockAddr oaddr;
274+ oaddr.ia = saddr;
275+ mcastAddrs.push_back(oaddr);
276+ continue;
277+ }
278+
279 stat = this->attachInterface (caNetAddr(saddr), autoBeaconAddr, configAddrOnceFlag);
280 if (stat) {
281 errMessage(stat, "unable to attach explicit interface");
282@@ -131,6 +144,10 @@
283 errMessage(stat, "unable to attach any interface");
284 }
285 }
286+
287+ for (mcastAddrs_t::const_iterator it = mcastAddrs.begin(); it!=mcastAddrs.end(); ++it) {
288+ this->addMCast(*it);
289+ }
290 }
291
292 //
293
294=== modified file 'src/ca/legacy/pcas/io/bsdSocket/caServerIO.h'
295--- src/ca/legacy/pcas/io/bsdSocket/caServerIO.h 2010-10-05 19:27:37 +0000
296+++ src/ca/legacy/pcas/io/bsdSocket/caServerIO.h 2016-02-25 16:34:00 +0000
297@@ -47,6 +47,8 @@
298 virtual caStatus attachInterface (
299 const caNetAddr & addr, bool autoBeaconAddr,
300 bool addConfigAddr ) = 0;
301+
302+ virtual void addMCast(const osiSockAddr&) = 0;
303 };
304
305 #endif // caServerIOh
306
307=== modified file 'src/ca/legacy/pcas/io/bsdSocket/casDGIntfIO.cc'
308--- src/ca/legacy/pcas/io/bsdSocket/casDGIntfIO.cc 2014-02-16 18:29:58 +0000
309+++ src/ca/legacy/pcas/io/bsdSocket/casDGIntfIO.cc 2016-02-25 16:34:00 +0000
310@@ -19,6 +19,11 @@
311 #include "addrList.h"
312 #include "errlog.h"
313
314+#if defined(_MSC_VER)
315+# include <BaseTsd.h>
316+ typedef SSIZE_T ssize_t;
317+#endif
318+
319 #define epicsExportSharedSymbols
320 #include "casDGIntfIO.h"
321 #include "ipIgnoreEntry.h"
322@@ -148,26 +153,8 @@
323 }
324
325 if ( addConfigBeaconAddr ) {
326-
327- //
328- // by default use EPICS_CA_ADDR_LIST for the
329- // beacon address list
330- //
331- const ENV_PARAM *pParam;
332-
333- if ( envGetConfigParamPtr ( & EPICS_CAS_INTF_ADDR_LIST ) ||
334- envGetConfigParamPtr ( & EPICS_CAS_BEACON_ADDR_LIST ) ) {
335- pParam = & EPICS_CAS_BEACON_ADDR_LIST;
336- }
337- else {
338- pParam = & EPICS_CA_ADDR_LIST;
339- }
340-
341- //
342- // add in the configured addresses
343- //
344 addAddrToChannelAccessAddressList (
345- & BCastAddrList, pParam, beaconPort, pParam == & EPICS_CA_ADDR_LIST );
346+ & BCastAddrList, &EPICS_CAS_BEACON_ADDR_LIST, beaconPort, 0 );
347 }
348
349 removeDuplicateAddresses ( & this->beaconAddrList, & BCastAddrList, 0 );
350@@ -267,7 +254,7 @@
351
352 // avoid use of ellFree because problems on windows occur if the
353 // free is in a different DLL than the malloc
354- ELLNODE * nnode = this->beaconAddrList.node.next;
355+ ELLNODE * nnode = ellFirst(&this->beaconAddrList);
356 while ( nnode )
357 {
358 ELLNODE * pnode = nnode;
359@@ -418,52 +405,24 @@
360 }
361
362 void casDGIntfIO::sendBeaconIO ( char & msg, unsigned length,
363- aitUint16 & portField, aitUint32 & addrField )
364+ aitUint16 & portField, aitUint32 & )
365 {
366 caNetAddr addr = this->serverAddress ();
367 struct sockaddr_in inetAddr = addr.getSockIP();
368- osiSockAddrNode *pAddr;
369- int status;
370- char buf[64];
371
372 portField = inetAddr.sin_port; // the TCP port
373
374- for (pAddr = reinterpret_cast <osiSockAddrNode *> ( ellFirst ( & this->beaconAddrList ) );
375- pAddr; pAddr = reinterpret_cast <osiSockAddrNode *> ( ellNext ( & pAddr->node ) ) ) {
376- status = connect ( this->beaconSock, &pAddr->addr.sa, sizeof ( pAddr->addr.sa ) );
377- if (status<0) {
378- char sockErrBuf[64];
379+ for (ELLNODE *pNode = ellFirst(&this->beaconAddrList); pNode; pNode = ellNext(pNode))
380+ {
381+ osiSockAddrNode *pAddr = reinterpret_cast<osiSockAddrNode *>(pNode);
382+
383+ ssize_t status = sendto(this->beaconSock, &msg, length, 0, &pAddr->addr.sa, sizeof(pAddr->addr.ia));
384+ if ( status != length ) {
385+ char sockErrBuf[64], buf[64];
386 epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
387- ipAddrToDottedIP ( & pAddr->addr.ia, buf, sizeof ( buf ) );
388- errlogPrintf ( "%s: CA beacon routing (connect to \"%s\") error was \"%s\"\n",
389- __FILE__, buf, sockErrBuf );
390- }
391- else {
392- osiSockAddr sockAddr;
393- osiSocklen_t size = ( osiSocklen_t ) sizeof ( sockAddr.sa );
394- status = getsockname ( this->beaconSock, &sockAddr.sa, &size );
395- if ( status < 0 ) {
396- char sockErrBuf[64];
397- epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
398- errlogPrintf ( "%s: CA beacon routing (getsockname) error was \"%s\"\n",
399- __FILE__, sockErrBuf );
400- }
401- else if ( sockAddr.sa.sa_family == AF_INET ) {
402- addrField = sockAddr.ia.sin_addr.s_addr;
403-
404- status = send ( this->beaconSock, &msg, length, 0 );
405- if ( status < 0 ) {
406- char sockErrBuf[64];
407- epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
408- ipAddrToA ( &pAddr->addr.ia, buf, sizeof(buf) );
409- errlogPrintf ( "%s: CA beacon (send to \"%s\") error was \"%s\"\n",
410- __FILE__, buf, sockErrBuf );
411- }
412- else {
413- unsigned statusAsLength = static_cast < unsigned > ( status );
414- assert ( statusAsLength == length );
415- }
416- }
417+ ipAddrToA ( &pAddr->addr.ia, buf, sizeof(buf) );
418+ errlogPrintf ( "%s: CA beacon (send to \"%s\") error was \"%s\" (%u)\n",
419+ __FILE__, buf, sockErrBuf, (unsigned)status );
420 }
421 }
422 }
423
424=== modified file 'src/ioc/db/dbChannel.c'
425--- src/ioc/db/dbChannel.c 2015-03-18 21:48:07 +0000
426+++ src/ioc/db/dbChannel.c 2016-02-25 16:34:00 +0000
427@@ -704,21 +704,27 @@
428 int pre = ellCount(&chan->pre_chain);
429 int post = ellCount(&chan->post_chain);
430
431- printf("%*schannel name: %s\n", indent, "", chan->name);
432- printf("%*s field_type=%s (%d bytes), dbr_type=%s, %ld element%s, %d filter%s", indent, "",
433- dbGetFieldTypeString(chan->addr.field_type), chan->addr.field_size,
434- dbGetFieldTypeString(chan->addr.dbr_field_type), elems, elems == 1 ? "" : "s",
435- count, count == 1 ? "" : "s");
436- if (count)
437- printf(" (%d pre eventq, %d post eventq)\n", pre, post);
438- else
439- printf("\n");
440- if (level > 0)
441- dbChannelFilterShow(chan, level - 1, indent + 2);
442- if (count) {
443- printf("%*s final field_type=%s (%dB), %ld element%s\n", indent, "",
444- dbGetFieldTypeString(chan->final_type), chan->final_field_size,
445- felems, felems == 1 ? "" : "s");
446+ printf("%*sChannel: '%s'\n", indent, "", chan->name);
447+ if (level > 0) {
448+ printf("%*sfield_type=%s (%d bytes), dbr_type=%s, %ld element%s",
449+ indent + 4, "",
450+ dbGetFieldTypeString(chan->addr.field_type),
451+ chan->addr.field_size,
452+ dbGetFieldTypeString(chan->addr.dbr_field_type),
453+ elems, elems == 1 ? "" : "s");
454+ if (count)
455+ printf("\n%*s%d filter%s (%d pre eventq, %d post eventq)\n",
456+ indent + 4, "", count, count == 1 ? "" : "s", pre, post);
457+ else
458+ printf(", no filters\n");
459+ if (level > 1)
460+ dbChannelFilterShow(chan, level - 2, indent + 8);
461+ if (count) {
462+ printf("%*sfinal field_type=%s (%dB), %ld element%s\n", indent + 4, "",
463+ dbGetFieldTypeString(chan->final_type),
464+ chan->final_field_size,
465+ felems, felems == 1 ? "" : "s");
466+ }
467 }
468 }
469
470
471=== modified file 'src/ioc/rsrv/camessage.c'
472--- src/ioc/rsrv/camessage.c 2015-04-22 21:51:31 +0000
473+++ src/ioc/rsrv/camessage.c 2016-02-25 16:34:00 +0000
474@@ -1126,7 +1126,7 @@
475 pclient = pciu->client;
476 assert(pclient);
477
478- if(pclient == prsrv_cast_client){
479+ if(pclient->proto==IPPROTO_UDP){
480 return;
481 }
482
483@@ -1193,7 +1193,7 @@
484 int v41;
485 int status;
486
487- assert ( pciu->client != prsrv_cast_client );
488+ assert ( pciu->client->proto!=IPPROTO_UDP );
489
490 /*
491 * noop if this is an old client
492@@ -1321,7 +1321,7 @@
493 }
494 }
495 else {
496- epicsMutexMustLock(prsrv_cast_client->chanListLock);
497+ epicsMutexMustLock(client->chanListLock);
498 /*
499 * clients which dont claim their
500 * channel in use block prior to
501@@ -1331,7 +1331,7 @@
502 if(!pciu){
503 errlogPrintf("CAS: client timeout disconnect id=%d\n",
504 mp->m_cid);
505- epicsMutexUnlock(prsrv_cast_client->chanListLock);
506+ epicsMutexUnlock(client->chanListLock);
507 SEND_LOCK(client);
508 send_err(
509 mp,
510@@ -1343,33 +1343,15 @@
511 }
512
513 /*
514- * duplicate claim message are unacceptable
515- * (so we disconnect the client)
516- */
517- if (pciu->client!=prsrv_cast_client) {
518- errlogPrintf("CAS: duplicate claim disconnect id=%d\n",
519- mp->m_cid);
520- epicsMutexUnlock(prsrv_cast_client->chanListLock);
521- SEND_LOCK(client);
522- send_err(
523- mp,
524- ECA_INTERNAL,
525- client,
526- "duplicate claim in old connect protocol");
527- SEND_UNLOCK(client);
528- return RSRV_ERROR;
529- }
530-
531- /*
532 * remove channel in use block from
533 * the UDP client where it could time
534 * out and place it on the client
535 * who is claiming it
536 */
537 ellDelete(
538- &prsrv_cast_client->chanList,
539+ &client->chanList,
540 &pciu->node);
541- epicsMutexUnlock(prsrv_cast_client->chanListLock);
542+ epicsMutexUnlock(client->chanListLock);
543
544 epicsMutexMustLock(client->chanListLock);
545 pciu->state = rsrvCS_pendConnectResp;
546@@ -2515,12 +2497,7 @@
547 unsigned bytes_left;
548 int status = RSRV_ERROR;
549
550- if ( ! pCaBucket ) {
551- pCaBucket = bucketCreate(CAS_HASH_TABLE_SIZE);
552- if(!pCaBucket){
553- return RSRV_ERROR;
554- }
555- }
556+ assert(pCaBucket);
557
558 /* drain remnents of large messages that will not fit */
559 if ( client->recvBytesToDrain ) {
560@@ -2623,7 +2600,7 @@
561 if ( CASDEBUG > 2 )
562 log_header (NULL, client, &msg, pBody, nmsg);
563
564- if ( client == prsrv_cast_client ) {
565+ if ( client->proto==IPPROTO_UDP ) {
566 if ( msg.m_cmmd < NELEMENTS ( udpJumpTable ) ) {
567 status = ( *udpJumpTable[msg.m_cmmd] )( &msg, pBody, client );
568 if (status!=RSRV_OK) {
569
570=== modified file 'src/ioc/rsrv/caserverio.c'
571--- src/ioc/rsrv/caserverio.c 2015-02-28 00:11:37 +0000
572+++ src/ioc/rsrv/caserverio.c 2016-02-25 16:34:00 +0000
573@@ -222,7 +222,7 @@
574 /*
575 * add placeholder for the first version message should it be needed
576 */
577- rsrv_version_reply ( prsrv_cast_client );
578+ rsrv_version_reply ( pclient );
579
580 SEND_UNLOCK(pclient);
581
582
583=== modified file 'src/ioc/rsrv/caservertask.c'
584--- src/ioc/rsrv/caservertask.c 2015-02-28 00:11:37 +0000
585+++ src/ioc/rsrv/caservertask.c 2016-02-25 16:34:00 +0000
586@@ -1,4 +1,7 @@
587 /*************************************************************************\
588+* Copyright (c) 2016 Michael Davidsaver
589+* Copyright (c) 2015 Brookhaven Science Assoc. as operator of Brookhaven
590+* National Laboratory.
591 * Copyright (c) 2002 The University of Chicago, as Operator of Argonne
592 * National Laboratory.
593 * Copyright (c) 2002 The Regents of the University of California, as
594@@ -32,6 +35,7 @@
595 #include "osiPoolStatus.h"
596 #include "osiSock.h"
597 #include "taskwd.h"
598+#include "cantProceed.h"
599
600 #define epicsExportSharedSymbols
601 #include "dbChannel.h"
602@@ -58,113 +62,12 @@
603 */
604 static void req_server (void *pParm)
605 {
606- unsigned priorityOfSelf = epicsThreadGetPrioritySelf ();
607- unsigned priorityOfBeacons;
608- epicsThreadBooleanStatus tbs;
609- osiSockAddrNode *pNode;
610- struct sockaddr_in serverAddr; /* server's address */
611- osiSocklen_t addrSize = (osiSocklen_t) sizeof(struct sockaddr_in);
612- int status;
613- SOCKET clientSock;
614- epicsThreadId tid;
615- int portChange;
616-
617- epicsSignalInstallSigPipeIgnore ();
618+ rsrv_iface_config *conf = pParm;
619+ SOCKET IOC_sock;
620
621 taskwdInsert ( epicsThreadGetIdSelf (), NULL, NULL );
622
623- rsrvCurrentClient = epicsThreadPrivateCreate ();
624-
625- if ( envGetConfigParamPtr ( &EPICS_CAS_SERVER_PORT ) ) {
626- ca_server_port = envGetInetPortConfigParam ( &EPICS_CAS_SERVER_PORT,
627- (unsigned short) CA_SERVER_PORT );
628- }
629- else {
630- ca_server_port = envGetInetPortConfigParam ( &EPICS_CA_SERVER_PORT,
631- (unsigned short) CA_SERVER_PORT );
632- }
633-
634- addAddrToChannelAccessAddressList ( &casIntfAddrList,
635- &EPICS_CAS_INTF_ADDR_LIST, ca_server_port, 0 );
636- if (ellCount(&casIntfAddrList) == 0) {
637- pNode = (osiSockAddrNode *) calloc ( 1, sizeof(*pNode) );
638- pNode->addr.ia.sin_family = AF_INET;
639- pNode->addr.ia.sin_addr.s_addr = htonl ( INADDR_ANY );
640- pNode->addr.ia.sin_port = htons ( ca_server_port );
641- ellAdd ( &casIntfAddrList, &pNode->node );
642- }
643- else {
644- if (ellCount ( &casIntfAddrList ) > 1)
645- epicsPrintf ("CAS: Multiple entries in EPICS_CAS_INTF_ADDR_LIST, "
646- "only the first will be used.\n");
647- pNode = (osiSockAddrNode *) ellFirst ( &casIntfAddrList );
648- }
649-
650- memcpy ( &serverAddr, &pNode->addr.ia, addrSize );
651-
652- if (IOC_sock != 0 && IOC_sock != INVALID_SOCKET) {
653- epicsSocketDestroy ( IOC_sock );
654- }
655-
656- /*
657- * Open the socket. Use ARPA Internet address format and stream
658- * sockets. Format described in <sys/socket.h>.
659- */
660- if ( ( IOC_sock = epicsSocketCreate (AF_INET, SOCK_STREAM, 0) ) == INVALID_SOCKET ) {
661- errlogPrintf ("CAS: Socket creation error\n");
662- epicsThreadSuspendSelf ();
663- }
664-
665- epicsSocketEnableAddressReuseDuringTimeWaitState ( IOC_sock );
666-
667- /* get server's Internet address */
668-
669- status = bind(IOC_sock, (struct sockaddr *) &serverAddr, addrSize);
670- if ( status < 0 ) {
671- if ( SOCKERRNO == SOCK_EADDRINUSE ) {
672- /*
673- * enable assignment of a default port
674- * (so the getsockname() call below will
675- * work correctly)
676- */
677- serverAddr.sin_port = ntohs (0);
678- status = bind(IOC_sock, (struct sockaddr *) &serverAddr, addrSize);
679- }
680- if ( status < 0 ) {
681- char sockErrBuf[64];
682- epicsSocketConvertErrnoToString (
683- sockErrBuf, sizeof ( sockErrBuf ) );
684- errlogPrintf ( "CAS: Socket bind error was \"%s\"\n",
685- sockErrBuf );
686- epicsThreadSuspendSelf ();
687- }
688- portChange = 1;
689- }
690- else {
691- portChange = 0;
692- }
693-
694- status = getsockname ( IOC_sock,
695- (struct sockaddr *)&serverAddr, &addrSize);
696- if ( status ) {
697- char sockErrBuf[64];
698- epicsSocketConvertErrnoToString (
699- sockErrBuf, sizeof ( sockErrBuf ) );
700- errlogPrintf ( "CAS: getsockname() error %s\n",
701- sockErrBuf );
702- epicsThreadSuspendSelf ();
703- }
704-
705- ca_server_port = ntohs (serverAddr.sin_port);
706-
707- if ( portChange ) {
708- errlogPrintf ( "cas warning: Configured TCP port was unavailable.\n");
709- errlogPrintf ( "cas warning: Using dynamically assigned TCP port %hu,\n",
710- ca_server_port );
711- errlogPrintf ( "cas warning: but now two or more servers share the same UDP port.\n");
712- errlogPrintf ( "cas warning: Depending on your IP kernel this server may not be\n" );
713- errlogPrintf ( "cas warning: reachable with UDP unicast (a host's IP in EPICS_CA_ADDR_LIST)\n" );
714- }
715+ IOC_sock = conf->tcp;
716
717 /* listen and accept new connections */
718 if ( listen ( IOC_sock, 20 ) < 0 ) {
719@@ -177,25 +80,10 @@
720 epicsThreadSuspendSelf ();
721 }
722
723- tbs = epicsThreadHighestPriorityLevelBelow ( priorityOfSelf, &priorityOfBeacons );
724- if ( tbs != epicsThreadBooleanStatusSuccess ) {
725- priorityOfBeacons = priorityOfSelf;
726- }
727-
728- beacon_startStopEvent = epicsEventMustCreate(epicsEventEmpty);
729- beacon_ctl = ctlPause;
730-
731- tid = epicsThreadCreate ( "CAS-beacon", priorityOfBeacons,
732- epicsThreadGetStackSize (epicsThreadStackSmall),
733- rsrv_online_notify_task, 0 );
734- if ( tid == 0 ) {
735- epicsPrintf ( "CAS: unable to start beacon thread\n" );
736- }
737-
738- epicsEventMustWait(beacon_startStopEvent);
739 epicsEventSignal(castcp_startStopEvent);
740
741 while (TRUE) {
742+ SOCKET clientSock;
743 struct sockaddr sockAddr;
744 osiSocklen_t addLen = sizeof(sockAddr);
745
746@@ -244,6 +132,327 @@
747 }
748 }
749
750+static
751+int tryBind(SOCKET sock, const osiSockAddr* addr, const char *name)
752+{
753+ if(bind(sock, (struct sockaddr *) &addr->sa, sizeof(*addr))<0) {
754+ char sockErrBuf[64];
755+ if(SOCKERRNO!=SOCK_EADDRINUSE)
756+ {
757+ epicsSocketConvertErrnoToString (
758+ sockErrBuf, sizeof ( sockErrBuf ) );
759+ errlogPrintf ( "CAS: %s bind error: \"%s\"\n",
760+ name, sockErrBuf );
761+ epicsThreadSuspendSelf ();
762+ }
763+ return -1;
764+ } else
765+ return 0;
766+}
767+
768+/* need to collect a set of TCP sockets, one for each interface,
769+ * which are bound to the same TCP port number.
770+ * Needed to avoid the complications and confusion of different TCP
771+ * ports for each interface (name server and beacon sender would need
772+ * to know this).
773+ */
774+static
775+SOCKET* rsrv_grab_tcp(unsigned short *port)
776+{
777+ SOCKET *socks;
778+ osiSockAddr scratch;
779+ unsigned i;
780+
781+ socks = mallocMustSucceed(ellCount(&casIntfAddrList)*sizeof(*socks), "rsrv_grab_tcp");
782+ for(i=0; i<ellCount(&casIntfAddrList); i++)
783+ socks[i] = INVALID_SOCKET;
784+
785+ /* start with preferred port */
786+ memset(&scratch, 0, sizeof(scratch));
787+ scratch.ia.sin_family = AF_INET;
788+ scratch.ia.sin_port = htons(*port);
789+
790+ while(ellCount(&casIntfAddrList)>0) {
791+ ELLNODE *cur, *next;
792+ unsigned ok = 1;
793+
794+ for(i=0; i<ellCount(&casIntfAddrList); i++) {
795+ if(socks[i] != INVALID_SOCKET)
796+ epicsSocketDestroy(socks[i]);
797+ socks[i] = INVALID_SOCKET;
798+ }
799+
800+ for (i=0, cur=ellFirst(&casIntfAddrList), next = cur ? ellNext(cur) : NULL;
801+ cur;
802+ i++, cur=next, next=next ? ellNext(next) : NULL)
803+ {
804+ SOCKET tcpsock;
805+ osiSockAddr ifaceAddr = ((osiSockAddrNode *)cur)->addr;
806+
807+ scratch.ia.sin_addr = ifaceAddr.ia.sin_addr;
808+
809+ tcpsock = socks[i] = epicsSocketCreate (AF_INET, SOCK_STREAM, 0);
810+ if(tcpsock==INVALID_SOCKET)
811+ cantProceed("rsrv ran out of sockets during initialization");
812+
813+ epicsSocketEnableAddressReuseDuringTimeWaitState ( tcpsock );
814+
815+ if(bind(tcpsock, &scratch.sa, sizeof(scratch))==0) {
816+ if(scratch.ia.sin_port==0) {
817+ /* use first socket to pick a random port */
818+ osiSocklen_t alen = sizeof(ifaceAddr);
819+ assert(i==0);
820+ if(getsockname(tcpsock, &ifaceAddr.sa, &alen)) {
821+ char sockErrBuf[64];
822+ epicsSocketConvertErrnoToString (
823+ sockErrBuf, sizeof ( sockErrBuf ) );
824+ errlogPrintf ( "CAS: getsockname error was \"%s\"\n",
825+ sockErrBuf );
826+ epicsThreadSuspendSelf ();
827+ ok = 0;
828+ break;
829+ }
830+ scratch.ia.sin_port = ifaceAddr.ia.sin_port;
831+ assert(scratch.ia.sin_port!=0);
832+ }
833+ } else {
834+ int errcode = SOCKERRNO;
835+ /* bind fails. React harshly to unexpected errors to avoid an infinite loop */
836+ if(errcode==SOCK_EADDRNOTAVAIL) {
837+ /* this is not a bind()able address. */
838+ int j;
839+ char name[40];
840+ ipAddrToDottedIP(&scratch.ia, name, sizeof(name));
841+ printf("Skipping %s which is not an interface address\n", name);
842+
843+ for(j=0; j<=i; j++) {
844+ epicsSocketDestroy(socks[j]);
845+ socks[j] = INVALID_SOCKET;
846+ }
847+
848+ ellDelete(&casIntfAddrList, cur);
849+ free(cur);
850+ ok = 0;
851+ break;
852+ }
853+ /* if SOCK_EADDRINUSE then try again with a different port number.
854+ * otherwise, fail hard
855+ */
856+ if(errcode!=SOCK_EADDRINUSE) {
857+ char name[40];
858+ char sockErrBuf[64];
859+ epicsSocketConvertErrnoToString (
860+ sockErrBuf, sizeof ( sockErrBuf ) );
861+ ipAddrToDottedIP(&scratch.ia, name, sizeof(name));
862+ cantProceed( "CAS: Socket bind %s error was \"%s\"\n",
863+ name, sockErrBuf );
864+ }
865+ ok = 0;
866+ break;
867+ }
868+ }
869+
870+ if (ok) {
871+ assert(scratch.ia.sin_port!=0);
872+ *port = ntohs(scratch.ia.sin_port);
873+
874+ break;
875+ } else {
876+
877+ for(i=0; i<ellCount(&casIntfAddrList); i++) {
878+ /* cleanup any ports actually bound */
879+ if(socks[i]!=INVALID_SOCKET) {
880+ epicsSocketDestroy(socks[i]);
881+ socks[i] = INVALID_SOCKET;
882+ }
883+ }
884+
885+ scratch.ia.sin_port=0; /* next iteration starts with a random port */
886+ }
887+ }
888+
889+ if(ellCount(&casIntfAddrList)==0)
890+ cantProceed("Error: RSRV has empty interface list\n"
891+ "The CA server can't function without binding to"
892+ " at least one network interface.\n");
893+
894+ return socks;
895+}
896+
897+static
898+void rsrv_build_addr_lists(void)
899+{
900+ int autobeaconlist = 1;
901+
902+ /* the UDP ports are known at this point, but the TCP port is not */
903+ assert(ca_beacon_port!=0);
904+ assert(ca_udp_port!=0);
905+
906+ envGetBoolConfigParam(&EPICS_CAS_AUTO_BEACON_ADDR_LIST, &autobeaconlist);
907+
908+ ellInit ( &casIntfAddrList );
909+ ellInit ( &beaconAddrList );
910+ ellInit ( &casMCastAddrList );
911+
912+ /* Setup socket for sending server beacons.
913+ * Also used for NIC introspection
914+ */
915+
916+ beaconSocket = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0);
917+ if (beaconSocket==INVALID_SOCKET)
918+ cantProceed("socket allocation failed during address list expansion");
919+
920+ {
921+ int intTrue = 1;
922+ if (setsockopt (beaconSocket, SOL_SOCKET, SO_BROADCAST,
923+ (char *)&intTrue, sizeof(intTrue))<0) {
924+ cantProceed("CAS: online socket set up error\n");
925+ }
926+#ifdef IP_ADD_MEMBERSHIP
927+ {
928+ int flag = 1;
929+ if (setsockopt(beaconSocket, IPPROTO_IP, IP_MULTICAST_LOOP,
930+ (char *)&flag, sizeof(flag))<0) {
931+ char sockErrBuf[64];
932+ epicsSocketConvertErrnoToString (
933+ sockErrBuf, sizeof ( sockErrBuf ) );
934+ errlogPrintf("rsrv: failed to set mcast loopback\n");
935+ }
936+ }
937+#endif
938+ }
939+
940+ /* populate the interface address list (default is empty) */
941+ {
942+ ELLLIST temp = ELLLIST_INIT;
943+ /* use the first parameter which is set. */
944+ addAddrToChannelAccessAddressList ( &temp, &EPICS_CAS_INTF_ADDR_LIST, ca_udp_port, 0 );
945+
946+ removeDuplicateAddresses(&casIntfAddrList, &temp, 0);
947+ }
948+
949+ /* Process the interface address list
950+ * Move multicast addresses to casMCastAddrList
951+ * Populate beacon address list (if autobeaconlist and iface list not-empty).
952+ */
953+ {
954+ int foundWildcard = 0, doautobeacon = autobeaconlist;
955+
956+ osiSockAddrNode *pNode, *pNext;
957+ for(pNode = (osiSockAddrNode*)ellFirst(&casIntfAddrList),
958+ pNext = pNode ? (osiSockAddrNode*)ellNext(&pNode->node) : NULL;
959+ pNode;
960+ pNode = pNext,
961+ pNext = pNext ? (osiSockAddrNode*)ellNext(&pNext->node) : NULL)
962+ {
963+ osiSockAddr match;
964+ epicsUInt32 top = ntohl(pNode->addr.ia.sin_addr.s_addr)>>24;
965+
966+ if(pNode->addr.ia.sin_family==AF_INET && pNode->addr.ia.sin_addr.s_addr==htonl(INADDR_ANY))
967+ {
968+ foundWildcard = 1;
969+
970+ } else if(pNode->addr.ia.sin_family==AF_INET && top>=224 && top<=239) {
971+ /* This is a multi-cast address */
972+ ellDelete(&casIntfAddrList, &pNode->node);
973+ ellAdd(&casMCastAddrList, &pNode->node);
974+ continue;
975+ }
976+
977+ if(!doautobeacon)
978+ continue;
979+ /* when given a specific interface address, auto populate with the
980+ * corresponding broadcast address.
981+ */
982+
983+ autobeaconlist = 0; /* prevent later population from wildcard */
984+
985+ memset(&match, 0, sizeof(match));
986+ match.ia.sin_family = AF_INET;
987+ match.ia.sin_addr.s_addr = pNode->addr.ia.sin_addr.s_addr;
988+ match.ia.sin_port = htons(ca_beacon_port);
989+
990+ osiSockDiscoverBroadcastAddresses(&beaconAddrList, beaconSocket, &match);
991+ }
992+
993+ if (foundWildcard && ellCount(&casIntfAddrList) != 1) {
994+ cantProceed("CAS interface address list can not contain 0.0.0.0 and other interface addresses.\n");
995+ }
996+ }
997+
998+ if (ellCount(&casIntfAddrList) == 0) {
999+ /* default to wildcard 0.0.0.0 when interface address list is empty */
1000+ osiSockAddrNode *pNode = (osiSockAddrNode *) callocMustSucceed( 1, sizeof(*pNode), "rsrv_init" );
1001+ pNode->addr.ia.sin_family = AF_INET;
1002+ pNode->addr.ia.sin_addr.s_addr = htonl ( INADDR_ANY );
1003+ pNode->addr.ia.sin_port = 0;
1004+ ellAdd ( &casIntfAddrList, &pNode->node );
1005+ }
1006+
1007+ {
1008+ ELLLIST temp = ELLLIST_INIT;
1009+ osiSockAddrNode *pNode;
1010+
1011+ ellConcat(&temp, &beaconAddrList);
1012+
1013+ /* collect user specified beacon address list
1014+ * prefer EPICS_CAS_BEACON_ADDR_LIST, fallback to EPICS_CA_ADDR_LIST
1015+ */
1016+ addAddrToChannelAccessAddressList ( &temp, &EPICS_CAS_BEACON_ADDR_LIST, ca_beacon_port, 0 );
1017+
1018+ if (autobeaconlist) {
1019+ /* auto populate with all broadcast addresses.
1020+ * Note that autobeaconlist is zeroed above if an interface
1021+ * address list is provided.
1022+ */
1023+ osiSockAddr match;
1024+ memset(&match, 0, sizeof(match));
1025+ match.ia.sin_family = AF_INET;
1026+ match.ia.sin_addr.s_addr = htonl(INADDR_ANY);
1027+ match.ia.sin_port = htons(ca_beacon_port);
1028+
1029+ osiSockDiscoverBroadcastAddresses(&temp, beaconSocket, &match);
1030+ }
1031+
1032+ /* set the port for any automatically discovered destinations. */
1033+ for(pNode = (osiSockAddrNode*)ellFirst(&temp);
1034+ pNode;
1035+ pNode = (osiSockAddrNode*)ellNext(&pNode->node))
1036+ {
1037+ if(pNode->addr.ia.sin_port==0)
1038+ pNode->addr.ia.sin_port = htons(ca_beacon_port);
1039+ }
1040+
1041+ removeDuplicateAddresses(&beaconAddrList, &temp, 0);
1042+ }
1043+
1044+ if (ellCount(&beaconAddrList)==0)
1045+ fprintf(stderr, "Warning: RSRV has empty beacon address list\n");
1046+
1047+ {
1048+ osiSockAddrNode *node;
1049+ ELLLIST temp = ELLLIST_INIT,
1050+ temp2= ELLLIST_INIT;
1051+ size_t idx = 0;
1052+
1053+ addAddrToChannelAccessAddressList ( &temp, &EPICS_CAS_IGNORE_ADDR_LIST, 0, 0 );
1054+ removeDuplicateAddresses(&temp2, &temp, 0);
1055+
1056+ /* Keep the list of addresses to ignore in an array on the assumption that
1057+ * it is short enough that using a hash table would be slower.
1058+ * 0.0.0.0 indicates end of list
1059+ */
1060+ casIgnoreAddrs = callocMustSucceed(1+ellCount(&temp2), sizeof(casIgnoreAddrs[0]), "casIgnoreAddrs");
1061+
1062+ while((node=(osiSockAddrNode*)ellGet(&temp2))!=NULL)
1063+ {
1064+ casIgnoreAddrs[idx++] = node->addr.ia.sin_addr.s_addr;
1065+ free(node);
1066+ }
1067+ casIgnoreAddrs[idx] = 0;
1068+ }
1069+}
1070+
1071 static dbServer rsrv_server = {
1072 ELLNODE_INIT,
1073 "rsrv",
1074@@ -257,11 +466,9 @@
1075 */
1076 int rsrv_init (void)
1077 {
1078- epicsThreadBooleanStatus tbs;
1079- unsigned priorityOfConnectDaemon;
1080- epicsThreadId tid;
1081 long maxBytesAsALong;
1082 long status;
1083+ SOCKET *socks;
1084
1085 clientQlock = epicsMutexMustCreate();
1086
1087@@ -272,8 +479,31 @@
1088 freeListInitPvt ( &rsrvSmallBufFreeListTCP, MAX_TCP, 16 );
1089 initializePutNotifyFreeList ();
1090
1091+ epicsSignalInstallSigPipeIgnore ();
1092+
1093+ rsrvCurrentClient = epicsThreadPrivateCreate ();
1094+
1095 dbRegisterServer(&rsrv_server);
1096
1097+ if ( envGetConfigParamPtr ( &EPICS_CAS_SERVER_PORT ) ) {
1098+ ca_server_port = envGetInetPortConfigParam ( &EPICS_CAS_SERVER_PORT,
1099+ (unsigned short) CA_SERVER_PORT );
1100+ }
1101+ else {
1102+ ca_server_port = envGetInetPortConfigParam ( &EPICS_CA_SERVER_PORT,
1103+ (unsigned short) CA_SERVER_PORT );
1104+ }
1105+ ca_udp_port = ca_server_port;
1106+
1107+ if (envGetConfigParamPtr(&EPICS_CAS_BEACON_PORT)) {
1108+ ca_beacon_port = envGetInetPortConfigParam (&EPICS_CAS_BEACON_PORT,
1109+ (unsigned short) CA_REPEATER_PORT );
1110+ }
1111+ else {
1112+ ca_beacon_port = envGetInetPortConfigParam (&EPICS_CA_REPEATER_PORT,
1113+ (unsigned short) CA_REPEATER_PORT );
1114+ }
1115+
1116 status = envGetLongConfigParam ( &EPICS_CA_MAX_ARRAY_BYTES, &maxBytesAsALong );
1117 if ( status || maxBytesAsALong < 0 ) {
1118 errlogPrintf ( "CAS: EPICS_CA_MAX_ARRAY_BYTES was not a positive integer\n" );
1119@@ -298,41 +528,223 @@
1120 }
1121 }
1122 freeListInitPvt ( &rsrvLargeBufFreeListTCP, rsrvSizeofLargeBufTCP, 1 );
1123- ellInit ( &casIntfAddrList );
1124- ellInit ( &beaconAddrList );
1125- prsrv_cast_client = NULL;
1126- pCaBucket = NULL;
1127+ pCaBucket = bucketCreate(CAS_HASH_TABLE_SIZE);
1128+ if (!pCaBucket)
1129+ cantProceed("RSRV failed to allocate ID lookup table\n");
1130+
1131+ rsrv_build_addr_lists();
1132
1133 castcp_startStopEvent = epicsEventMustCreate(epicsEventEmpty);
1134+ casudp_startStopEvent = epicsEventMustCreate(epicsEventEmpty);
1135+ beacon_startStopEvent = epicsEventMustCreate(epicsEventEmpty);
1136 castcp_ctl = ctlPause;
1137
1138- /*
1139- * go down two levels so that we are below
1140- * the TCP and event threads started on behalf
1141- * of individual clients
1142- */
1143- tbs = epicsThreadHighestPriorityLevelBelow (
1144- epicsThreadPriorityCAServerLow, &priorityOfConnectDaemon );
1145- if ( tbs == epicsThreadBooleanStatusSuccess ) {
1146- tbs = epicsThreadHighestPriorityLevelBelow (
1147- priorityOfConnectDaemon, &priorityOfConnectDaemon );
1148- if ( tbs != epicsThreadBooleanStatusSuccess ) {
1149- priorityOfConnectDaemon = epicsThreadPriorityCAServerLow;
1150- }
1151- }
1152- else {
1153- priorityOfConnectDaemon = epicsThreadPriorityCAServerLow;
1154- }
1155-
1156- tid = epicsThreadCreate ( "CAS-TCP",
1157- priorityOfConnectDaemon,
1158- epicsThreadGetStackSize(epicsThreadStackMedium),
1159- req_server, 0);
1160- if ( tid == 0 ) {
1161- epicsPrintf ( "CAS: unable to start connection request thread\n" );
1162- }
1163-
1164- epicsEventMustWait(castcp_startStopEvent);
1165+ /* Thread priorites
1166+ * Now starting per interface
1167+ * TCP Listener: epicsThreadPriorityCAServerLow-2
1168+ * Name receiver: epicsThreadPriorityCAServerLow-4
1169+ * Now starting global
1170+ * Beacon sender: epicsThreadPriorityCAServerLow-3
1171+ * Started later per TCP client
1172+ * TCP receiver: epicsThreadPriorityCAServerLow
1173+ * TCP sender : epicsThreadPriorityCAServerLow-1
1174+ */
1175+ {
1176+ unsigned i;
1177+ threadPrios[0] = epicsThreadPriorityCAServerLow;
1178+
1179+ for(i=1; i<NELEMENTS(threadPrios); i++)
1180+ {
1181+ if(epicsThreadBooleanStatusSuccess!=epicsThreadHighestPriorityLevelBelow(
1182+ threadPrios[i-1], &threadPrios[i]))
1183+ {
1184+ /* on failure use the lowest known */
1185+ threadPrios[i] = threadPrios[i-1];
1186+ }
1187+ }
1188+ }
1189+
1190+ {
1191+ unsigned short sport = ca_server_port;
1192+ socks = rsrv_grab_tcp(&sport);
1193+
1194+ if ( sport != ca_server_port ) {
1195+ ca_server_port = sport;
1196+ errlogPrintf ( "cas warning: Configured TCP port was unavailable.\n");
1197+ errlogPrintf ( "cas warning: Using dynamically assigned TCP port %hu,\n",
1198+ ca_server_port );
1199+ errlogPrintf ( "cas warning: but now two or more servers share the same UDP port.\n");
1200+ errlogPrintf ( "cas warning: Depending on your IP kernel this server may not be\n" );
1201+ errlogPrintf ( "cas warning: reachable with UDP unicast (a host's IP in EPICS_CA_ADDR_LIST)\n" );
1202+ }
1203+ }
1204+
1205+ /* start servers (TCP and UDP(s) for each interface.
1206+ */
1207+ {
1208+ int havesometcp = 0;
1209+ ELLNODE *cur;
1210+ int i;
1211+
1212+ for (i=0, cur=ellFirst(&casIntfAddrList); cur; i++, cur=ellNext(cur))
1213+ {
1214+ char ifaceName[40];
1215+ rsrv_iface_config *conf;
1216+
1217+ conf = callocMustSucceed(1, sizeof(*conf), "rsrv_init");
1218+
1219+ conf->tcpAddr = ((osiSockAddrNode *)cur)->addr;
1220+ conf->tcpAddr.ia.sin_port = htons(ca_server_port);
1221+ conf->tcp = socks[i];
1222+ socks[i] = INVALID_SOCKET;
1223+
1224+ ipAddrToDottedIP (&conf->tcpAddr.ia, ifaceName, sizeof(ifaceName));
1225+
1226+ conf->udp = conf->udpbcast = INVALID_SOCKET;
1227+
1228+ /* create and bind UDP name receiver socket(s) */
1229+
1230+ conf->udp = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0);
1231+ if(conf->udp==INVALID_SOCKET)
1232+ cantProceed("rsrv_init ran out of udp sockets");
1233+
1234+ conf->udpAddr = conf->tcpAddr;
1235+ conf->udpAddr.ia.sin_port = htons(ca_udp_port);
1236+
1237+ epicsSocketEnableAddressUseForDatagramFanout ( conf->udp );
1238+
1239+ if(tryBind(conf->udp, &conf->udpAddr, "UDP unicast socket"))
1240+ goto cleanup;
1241+
1242+#ifdef IP_ADD_MEMBERSHIP
1243+ /* join UDP socket to any multicast groups */
1244+ {
1245+ osiSockAddrNode *pNode;
1246+
1247+ for(pNode = (osiSockAddrNode*)ellFirst(&casMCastAddrList);
1248+ pNode;
1249+ pNode = (osiSockAddrNode*)ellNext(&pNode->node))
1250+ {
1251+ struct ip_mreq mreq;
1252+
1253+ memset(&mreq, 0, sizeof(mreq));
1254+ mreq.imr_multiaddr = pNode->addr.ia.sin_addr;
1255+ mreq.imr_interface.s_addr = conf->udpAddr.ia.sin_addr.s_addr;
1256+
1257+ if (setsockopt(conf->udp, IPPROTO_IP, IP_ADD_MEMBERSHIP,
1258+ (char *) &mreq, sizeof(mreq))!=0) {
1259+ struct sockaddr_in temp;
1260+ char name[40];
1261+ char sockErrBuf[64];
1262+ temp.sin_family = AF_INET;
1263+ temp.sin_addr = mreq.imr_multiaddr;
1264+ temp.sin_port = conf->udpAddr.ia.sin_port;
1265+ epicsSocketConvertErrnoToString (
1266+ sockErrBuf, sizeof ( sockErrBuf ) );
1267+ ipAddrToDottedIP (&temp, name, sizeof(name));
1268+ fprintf(stderr, "CAS: Socket mcast join %s to %s failed with \"%s\"\n",
1269+ ifaceName, name, sockErrBuf );
1270+ }
1271+ }
1272+ }
1273+#else
1274+ if(ellCount(&casMCastAddrList)){
1275+ fprintf(stderr, "IPv4 Multicast name lookup not supported by this target\n");
1276+ }
1277+#endif
1278+
1279+#if !(defined(_WIN32) || defined(__CYGWIN32__))
1280+ /* An oddness of BSD sockets (not winsock) is that binding to
1281+ * INADDR_ANY will receive unicast and broadcast, but binding to
1282+ * a specific interface address receives only unicast. The trick
1283+ * is to bind a second socket to the interface broadcast address,
1284+ * which will then receive only broadcasts.
1285+ */
1286+ if(conf->udpAddr.ia.sin_addr.s_addr!=htonl(INADDR_ANY)) {
1287+ /* find interface broadcast address */
1288+ ELLLIST bcastList = ELLLIST_INIT;
1289+ osiSockAddrNode *pNode;
1290+
1291+ osiSockDiscoverBroadcastAddresses (&bcastList,
1292+ conf->udp, &conf->udpAddr); // match addr
1293+
1294+ if(ellCount(&bcastList)==0) {
1295+ fprintf(stderr, "Warning: Can't find broadcast address of interface %s\n"
1296+ " Name lookup may not work on this interface\n", ifaceName);
1297+ } else {
1298+ if(ellCount(&bcastList)>1 && conf->udpAddr.ia.sin_addr.s_addr!=htonl(INADDR_ANY))
1299+ printf("Interface %s has more than one broadcast address?\n", ifaceName);
1300+
1301+ pNode = (osiSockAddrNode*)ellFirst(&bcastList);
1302+
1303+ conf->udpbcast = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0);
1304+ if(conf->udpbcast==INVALID_SOCKET)
1305+ cantProceed("rsrv_init ran out of udp sockets for bcast");
1306+
1307+ epicsSocketEnableAddressUseForDatagramFanout ( conf->udpbcast );
1308+
1309+ conf->udpbcastAddr = conf->udpAddr;
1310+ conf->udpbcastAddr.ia.sin_addr.s_addr = pNode->addr.ia.sin_addr.s_addr;
1311+
1312+ if(tryBind(conf->udpbcast, &conf->udpbcastAddr, "UDP Socket bcast"))
1313+ goto cleanup;
1314+ }
1315+
1316+ ellFree(&bcastList);
1317+ }
1318+
1319+#endif /* !(defined(_WIN32) || defined(__CYGWIN32__)) */
1320+
1321+ ellAdd(&servers, &conf->node);
1322+
1323+ /* have all sockets, time to start some threads */
1324+
1325+ epicsThreadMustCreate("CAS-TCP", threadPrios[2],
1326+ epicsThreadGetStackSize(epicsThreadStackMedium),
1327+ &req_server, conf);
1328+
1329+ epicsEventMustWait(castcp_startStopEvent);
1330+
1331+ epicsThreadMustCreate("CAS-UDP", threadPrios[4],
1332+ epicsThreadGetStackSize(epicsThreadStackMedium),
1333+ &cast_server, conf);
1334+
1335+ epicsEventMustWait(casudp_startStopEvent);
1336+
1337+#if !(defined(_WIN32) || defined(__CYGWIN32__))
1338+ if(conf->udpbcast != INVALID_SOCKET) {
1339+ conf->startbcast = 1;
1340+
1341+ epicsThreadMustCreate("CAS-UDP2", threadPrios[4],
1342+ epicsThreadGetStackSize(epicsThreadStackMedium),
1343+ &cast_server, conf);
1344+
1345+ epicsEventMustWait(casudp_startStopEvent);
1346+
1347+ conf->startbcast = 0;
1348+ }
1349+#endif /* !(defined(_WIN32) || defined(__CYGWIN32__)) */
1350+
1351+ havesometcp = 1;
1352+ continue;
1353+ cleanup:
1354+ epicsSocketDestroy(conf->tcp);
1355+ if(conf->udp!=INVALID_SOCKET) epicsSocketDestroy(conf->udp);
1356+ if(conf->udpbcast!=INVALID_SOCKET) epicsSocketDestroy(conf->udpbcast);
1357+ free(conf);
1358+ }
1359+
1360+ if(!havesometcp)
1361+ cantProceed("CAS: No TCP server started\n");
1362+ }
1363+
1364+ /* servers list is considered read-only from this point */
1365+
1366+ epicsThreadMustCreate("CAS-beacon", threadPrios[3],
1367+ epicsThreadGetStackSize(epicsThreadStackSmall),
1368+ &rsrv_online_notify_task, NULL);
1369+
1370+ epicsEventMustWait(beacon_startStopEvent);
1371
1372 return RSRV_OK;
1373 }
1374@@ -382,10 +794,11 @@
1375 pciu = (struct channel_in_use *) pList->node.next;
1376 while ( pciu ){
1377 dbChannelShow ( pciu->dbch, level, 8 );
1378- printf( " # on eventq=%d, access=%c%c\n",
1379- ellCount ( &pciu->eventq ),
1380- asCheckGet ( pciu->asClientPVT ) ? 'r': '-',
1381- rsrvCheckPut ( pciu ) ? 'w': '-' );
1382+ if ( level >= 1u )
1383+ printf( "%12s# on eventq=%d, access=%c%c\n", "",
1384+ ellCount ( &pciu->eventq ),
1385+ asCheckGet ( pciu->asClientPVT ) ? 'r': '-',
1386+ rsrvCheckPut ( pciu ) ? 'w': '-' );
1387 pciu = ( struct channel_in_use * ) ellNext ( &pciu->node );
1388 }
1389 epicsMutexUnlock ( client->chanListLock );
1390@@ -396,78 +809,82 @@
1391 */
1392 static void log_one_client (struct client *client, unsigned level)
1393 {
1394- char *pproto;
1395- double send_delay;
1396- double recv_delay;
1397- char *state[] = {"up", "down"};
1398- epicsTimeStamp current;
1399- char clientHostName[256];
1400-
1401- ipAddrToDottedIP (&client->addr, clientHostName, sizeof(clientHostName));
1402-
1403- if(client->proto == IPPROTO_UDP){
1404- pproto = "UDP";
1405- }
1406- else if(client->proto == IPPROTO_TCP){
1407- pproto = "TCP";
1408- }
1409- else{
1410- pproto = "UKN";
1411- }
1412-
1413- epicsTimeGetCurrent(&current);
1414- send_delay = epicsTimeDiffInSeconds(&current,&client->time_at_last_send);
1415- recv_delay = epicsTimeDiffInSeconds(&current,&client->time_at_last_recv);
1416-
1417- printf ( "%s %s(%s): User=\"%s\", V%u.%u, %d Channels, Priority=%u\n",
1418- pproto,
1419- clientHostName,
1420- client->pHostName ? client->pHostName : "",
1421+ char clientIP[40];
1422+ int n;
1423+
1424+ ipAddrToDottedIP (&client->addr, clientIP, sizeof(clientIP));
1425+
1426+ if ( client->proto == IPPROTO_UDP ) {
1427+ printf ( "\tLast name requested by %s:\n",
1428+ clientIP );
1429+ }
1430+ else if ( client->proto == IPPROTO_TCP ) {
1431+ printf ( " TCP client at %s '%s':\n",
1432+ clientIP,
1433+ client->pHostName ? client->pHostName : "" );
1434+ }
1435+ else {
1436+ printf ( " Unknown client at %s '%s':\n",
1437+ clientIP,
1438+ client->pHostName ? client->pHostName : "" );
1439+ }
1440+
1441+ n = ellCount(&client->chanList) + ellCount(&client->chanPendingUpdateARList);
1442+ printf ( "\tUser '%s', V%u.%u, Priority = %u, %d Channel%s\n",
1443 client->pUserName ? client->pUserName : "",
1444 CA_MAJOR_PROTOCOL_REVISION,
1445 client->minor_version_number,
1446- ellCount(&client->chanList) +
1447- ellCount(&client->chanPendingUpdateARList),
1448- client->priority );
1449- if ( level >= 1 ) {
1450- printf ("\tTask Id=%p, Socket FD=%d\n",
1451+ client->priority,
1452+ n, n == 1 ? "" : "s" );
1453+
1454+ if ( level >= 3u ) {
1455+ double send_delay;
1456+ double recv_delay;
1457+ char *state[] = {"up", "down"};
1458+ epicsTimeStamp current;
1459+
1460+ epicsTimeGetCurrent(&current);
1461+ send_delay = epicsTimeDiffInSeconds(&current,&client->time_at_last_send);
1462+ recv_delay = epicsTimeDiffInSeconds(&current,&client->time_at_last_recv);
1463+
1464+ printf ("\tTask Id = %p, Socket FD = %d\n",
1465 (void *) client->tid, client->sock);
1466 printf(
1467- "\tSecs since last send %6.2f, Secs since last receive %6.2f\n",
1468+ "\t%.2f secs since last send, %.2f secs since last receive\n",
1469 send_delay, recv_delay);
1470 printf(
1471- "\tUnprocessed request bytes=%u, Undelivered response bytes=%u\n",
1472+ "\tUnprocessed request bytes = %u, Undelivered response bytes = %u\n",
1473 client->recv.cnt - client->recv.stk,
1474 client->send.stk );
1475 printf(
1476- "\tState=%s%s%s\n",
1477+ "\tState = %s%s%s\n",
1478 state[client->disconnect?1:0],
1479 client->send.type == mbtLargeTCP ? " jumbo-send-buf" : "",
1480 client->recv.type == mbtLargeTCP ? " jumbo-recv-buf" : "");
1481 }
1482
1483- if ( level >= 2u ) {
1484- unsigned bytes_reserved = 0;
1485- bytes_reserved += sizeof(struct client);
1486+ if ( level >= 1u ) {
1487+ showChanList ( client, level - 1u, & client->chanList );
1488+ showChanList ( client, level - 1u, & client->chanPendingUpdateARList );
1489+ }
1490+
1491+ if ( level >= 4u ) {
1492+ unsigned bytes_reserved = sizeof(struct client);
1493+
1494 bytes_reserved += countChanListBytes (
1495 client, & client->chanList );
1496 bytes_reserved += countChanListBytes (
1497 client, & client->chanPendingUpdateARList );
1498 printf( "\t%d bytes allocated\n", bytes_reserved);
1499- showChanList ( client, level - 2u, & client->chanList );
1500- showChanList ( client, level - 2u, & client->chanPendingUpdateARList );
1501- }
1502-
1503- if ( level >= 3u ) {
1504- printf( "\tSend Lock\n");
1505+ printf( "\tSend Lock:\n\t ");
1506 epicsMutexShow(client->lock,1);
1507- printf( "\tPut Notify Lock\n");
1508+ printf( "\tPut Notify Lock:\n\t ");
1509 epicsMutexShow (client->putNotifyLock,1);
1510- printf( "\tAddress Queue Lock\n");
1511+ printf( "\tAddress Queue Lock:\n\t ");
1512 epicsMutexShow (client->chanListLock,1);
1513- printf( "\tEvent Queue Lock\n");
1514+ printf( "\tEvent Queue Lock:\n\t ");
1515 epicsMutexShow (client->eventqLock,1);
1516- printf( "\tBlock Semaphore\n");
1517+ printf( "\tBlock Semaphore:\n\t ");
1518 epicsEventShow (client->blockSem,1);
1519 }
1520 }
1521@@ -478,7 +895,7 @@
1522 void casr (unsigned level)
1523 {
1524 size_t bytes_reserved;
1525- struct client *client;
1526+ int n;
1527
1528 if ( ! clientQlock ) {
1529 return;
1530@@ -488,25 +905,106 @@
1531 CA_VERSION_STRING ( CA_MINOR_PROTOCOL_REVISION ) );
1532
1533 LOCK_CLIENTQ
1534- client = (struct client *) ellNext ( &clientQ.node );
1535- if (client) {
1536- printf("Connected circuits:\n");
1537- }
1538- else {
1539+ n = ellCount ( &clientQ );
1540+ if (n == 0) {
1541 printf("No clients connected.\n");
1542 }
1543- while (client) {
1544- log_one_client(client, level);
1545- client = (struct client *) ellNext(&client->node);
1546+ else if (level == 0) {
1547+ printf("%d client%s connected.\n",
1548+ n, n == 1 ? "" : "s" );
1549+ }
1550+ else {
1551+ struct client *client = (struct client *) ellFirst ( &clientQ );
1552+
1553+ printf("%d client%s connected:\n",
1554+ n, n == 1 ? "" : "s" );
1555+ while (client) {
1556+ log_one_client(client, level - 1);
1557+ client = (struct client *) ellNext(&client->node);
1558+ }
1559 }
1560 UNLOCK_CLIENTQ
1561
1562- if (level>=2 && prsrv_cast_client) {
1563- printf( "UDP Server:\n" );
1564- log_one_client(prsrv_cast_client, level);
1565- }
1566-
1567- if (level>=2u) {
1568+ if (level>=1) {
1569+ rsrv_iface_config *iface = (rsrv_iface_config *) ellFirst ( &servers );
1570+ while (iface) {
1571+ char buf[40];
1572+
1573+ ipAddrToDottedIP (&iface->tcpAddr.ia, buf, sizeof(buf));
1574+ printf("CAS-TCP server on %s with\n", buf);
1575+
1576+ ipAddrToDottedIP (&iface->udpAddr.ia, buf, sizeof(buf));
1577+#if defined(_WIN32)
1578+ printf(" CAS-UDP name server on %s\n", buf);
1579+ if (level >= 2)
1580+ log_one_client(iface->client, level - 2);
1581+#else
1582+ if (iface->udpbcast==INVALID_SOCKET) {
1583+ printf(" CAS-UDP name server on %s\n", buf);
1584+ if (level >= 2)
1585+ log_one_client(iface->client, level - 2);
1586+ }
1587+ else {
1588+ printf(" CAS-UDP unicast name server on %s\n", buf);
1589+ if (level >= 2)
1590+ log_one_client(iface->client, level - 2);
1591+ ipAddrToDottedIP (&iface->udpbcastAddr.ia, buf, sizeof(buf));
1592+ printf(" CAS-UDP broadcast name server on %s\n", buf);
1593+ if (level >= 2)
1594+ log_one_client(iface->bclient, level - 2);
1595+ }
1596+#endif
1597+
1598+ iface = (rsrv_iface_config *) ellNext(&iface->node);
1599+ }
1600+ }
1601+
1602+ if (level>=1) {
1603+ osiSockAddrNode * pAddr;
1604+ char buf[40];
1605+ int n = ellCount(&casMCastAddrList);
1606+
1607+ if (n) {
1608+ printf("Monitoring %d multicast address%s:\n",
1609+ n, n == 1 ? "" : "es");
1610+ for(pAddr = (osiSockAddrNode*)ellFirst(&casMCastAddrList);
1611+ pAddr;
1612+ pAddr = (osiSockAddrNode*)ellNext(&pAddr->node))
1613+ {
1614+ ipAddrToDottedIP (&pAddr->addr.ia, buf, sizeof(buf));
1615+ printf(" %s\n", buf);
1616+ }
1617+ }
1618+
1619+ n = ellCount(&beaconAddrList);
1620+ printf("Sending CAS-beacons to %d address%s:\n",
1621+ n, n == 1 ? "" : "es");
1622+ for(pAddr = (osiSockAddrNode*)ellFirst(&beaconAddrList);
1623+ pAddr;
1624+ pAddr = (osiSockAddrNode*)ellNext(&pAddr->node))
1625+ {
1626+ ipAddrToDottedIP (&pAddr->addr.ia, buf, sizeof(buf));
1627+ printf(" %s\n", buf);
1628+ }
1629+
1630+ if (casIgnoreAddrs[0]) { /* 0 indicates end of array */
1631+ size_t i;
1632+ printf("Ignoring UDP messages from address%s\n",
1633+ n == 1 ? "" : "es");
1634+ for(i=0; casIgnoreAddrs[i]; i++)
1635+ {
1636+ struct sockaddr_in addr;
1637+ memset(&addr, 0, sizeof(addr));
1638+ addr.sin_family = AF_INET;
1639+ addr.sin_addr.s_addr = casIgnoreAddrs[i];
1640+ addr.sin_port = 0;
1641+ ipAddrToDottedIP(&addr, buf, sizeof(buf));
1642+ printf(" %s\n", buf);
1643+ }
1644+ }
1645+ }
1646+
1647+ if (level>=4u) {
1648 bytes_reserved = 0u;
1649 bytes_reserved += sizeof (struct client) *
1650 freeListItemsAvail (rsrvClientFreeList);
1651@@ -520,28 +1018,22 @@
1652 freeListItemsAvail ( rsrvLargeBufFreeListTCP );
1653 bytes_reserved += rsrvSizeOfPutNotify ( 0 ) *
1654 freeListItemsAvail ( rsrvPutNotifyFreeList );
1655- printf( "There are currently %u bytes on the server's free list\n",
1656+ printf( "Free-lists total %u bytes, comprising\n",
1657 (unsigned int) bytes_reserved);
1658- printf( "%u client(s), %u channel(s), %u event(s) (monitors) %u putNotify(s)\n",
1659+ printf( " %u client(s), %u channel(s), %u monitor event(s), %u putNotify(s)\n",
1660 (unsigned int) freeListItemsAvail ( rsrvClientFreeList ),
1661 (unsigned int) freeListItemsAvail ( rsrvChanFreeList ),
1662 (unsigned int) freeListItemsAvail ( rsrvEventFreeList ),
1663 (unsigned int) freeListItemsAvail ( rsrvPutNotifyFreeList ));
1664- printf( "%u small buffers (%u bytes ea), and %u jumbo buffers (%u bytes ea)\n",
1665+ printf( " %u small (%u byte) buffers, %u jumbo (%u byte) buffers\n",
1666 (unsigned int) freeListItemsAvail ( rsrvSmallBufFreeListTCP ),
1667 MAX_TCP,
1668 (unsigned int) freeListItemsAvail ( rsrvLargeBufFreeListTCP ),
1669 rsrvSizeofLargeBufTCP );
1670- if(pCaBucket){
1671- printf( "The server's resource id conversion table:\n");
1672- LOCK_CLIENTQ;
1673- bucketShow (pCaBucket);
1674- UNLOCK_CLIENTQ;
1675- }
1676- printf ( "The server's array size limit is %u bytes max\n",
1677- rsrvSizeofLargeBufTCP );
1678-
1679- printChannelAccessAddressList (&beaconAddrList);
1680+ printf( "Server resource id table:\n");
1681+ LOCK_CLIENTQ;
1682+ bucketShow (pCaBucket);
1683+ UNLOCK_CLIENTQ;
1684 }
1685 }
1686
1687
1688=== modified file 'src/ioc/rsrv/cast_server.c'
1689--- src/ioc/rsrv/cast_server.c 2014-10-06 05:57:02 +0000
1690+++ src/ioc/rsrv/cast_server.c 2016-02-25 16:34:00 +0000
1691@@ -54,7 +54,7 @@
1692 /*
1693 * clean_addrq
1694 */
1695-static void clean_addrq(void)
1696+static void clean_addrq(struct client *client)
1697 {
1698 struct channel_in_use * pciu;
1699 struct channel_in_use * pnextciu;
1700@@ -67,9 +67,9 @@
1701
1702 epicsTimeGetCurrent ( &current );
1703
1704- epicsMutexMustLock ( prsrv_cast_client->chanListLock );
1705+ epicsMutexMustLock ( client->chanListLock );
1706 pnextciu = (struct channel_in_use *)
1707- prsrv_cast_client->chanList.node.next;
1708+ client->chanList.node.next;
1709
1710 while( (pciu = pnextciu) ) {
1711 pnextciu = (struct channel_in_use *)pciu->node.next;
1712@@ -77,7 +77,7 @@
1713 delay = epicsTimeDiffInSeconds(&current,&pciu->time_at_creation);
1714 if (delay > timeout) {
1715
1716- ellDelete(&prsrv_cast_client->chanList, &pciu->node);
1717+ ellDelete(&client->chanList, &pciu->node);
1718 LOCK_CLIENTQ;
1719 s = bucketRemoveItemUnsignedId (
1720 pCaBucket,
1721@@ -96,7 +96,7 @@
1722 if(delay>maxdelay) maxdelay = delay;
1723 }
1724 }
1725- epicsMutexUnlock ( prsrv_cast_client->chanListLock );
1726+ epicsMutexUnlock ( client->chanListLock );
1727
1728 # ifdef DEBUG
1729 if(ndelete){
1730@@ -115,70 +115,19 @@
1731 */
1732 void cast_server(void *pParm)
1733 {
1734- osiSockAddrNode *paddrNode;
1735- struct sockaddr_in sin;
1736+ rsrv_iface_config *conf = pParm;
1737 int status;
1738 int count=0;
1739+ int mysocket=0;
1740 struct sockaddr_in new_recv_addr;
1741 osiSocklen_t recv_addr_size;
1742 osiSockIoctl_t nchars;
1743+ SOCKET recv_sock, reply_sock;
1744+ struct client *client;
1745
1746 recv_addr_size = sizeof(new_recv_addr);
1747
1748- if( IOC_cast_sock!=0 && IOC_cast_sock!=INVALID_SOCKET ) {
1749- epicsSocketDestroy ( IOC_cast_sock );
1750- }
1751-
1752- /*
1753- * Open the socket.
1754- * Use ARPA Internet address format and datagram socket.
1755- */
1756-
1757- if ( ( IOC_cast_sock = epicsSocketCreate (AF_INET, SOCK_DGRAM, 0) ) == INVALID_SOCKET ) {
1758- epicsPrintf ("CAS: cast socket creation error\n");
1759- epicsThreadSuspendSelf ();
1760- }
1761-
1762- /*
1763- * some concern that vxWorks will run out of mBuf's
1764- * if this change is made
1765- *
1766- * joh 11-10-98
1767- */
1768-#if 0
1769- {
1770- /*
1771- *
1772- * this allows for faster connects by queuing
1773- * additional incomming UDP search frames
1774- *
1775- * this allocates a 32k buffer
1776- * (uses a power of two)
1777- */
1778- int size = 1u<<15u;
1779- status = setsockopt (IOC_cast_sock, SOL_SOCKET,
1780- SO_RCVBUF, (char *)&size, sizeof(size));
1781- if (status<0) {
1782- epicsPrintf ("CAS: unable to set cast socket size\n");
1783- }
1784- }
1785-#endif
1786-
1787- epicsSocketEnableAddressUseForDatagramFanout ( IOC_cast_sock );
1788-
1789- paddrNode = (osiSockAddrNode *) ellFirst ( &casIntfAddrList );
1790-
1791- memcpy(&sin, &paddrNode->addr.ia, sizeof (sin));
1792-
1793- /* get server's Internet address */
1794- if( bind(IOC_cast_sock, (struct sockaddr *)&sin, sizeof (sin)) < 0){
1795- char sockErrBuf[64];
1796- epicsSocketConvertErrnoToString (
1797- sockErrBuf, sizeof ( sockErrBuf ) );
1798- epicsPrintf ("CAS: UDP server port bind error was \"%s\"\n", sockErrBuf );
1799- epicsSocketDestroy ( IOC_cast_sock );
1800- epicsThreadSuspendSelf ();
1801- }
1802+ reply_sock = conf->udp;
1803
1804 /*
1805 * setup new client structure but reuse old structure if
1806@@ -186,27 +135,39 @@
1807 *
1808 */
1809 while ( TRUE ) {
1810- prsrv_cast_client = create_client ( IOC_cast_sock, IPPROTO_UDP );
1811- if ( prsrv_cast_client ) {
1812+ client = create_client ( reply_sock, IPPROTO_UDP );
1813+ if ( client ) {
1814 break;
1815 }
1816 epicsThreadSleep(300.0);
1817 }
1818+ if (conf->startbcast) {
1819+ recv_sock = conf->udpbcast;
1820+ conf->bclient = client;
1821+ }
1822+ else {
1823+ recv_sock = conf->udp;
1824+ conf->client = client;
1825+ }
1826+ client->udpRecv = recv_sock;
1827
1828- casAttachThreadToClient ( prsrv_cast_client );
1829+ casAttachThreadToClient ( client );
1830
1831 /*
1832 * add placeholder for the first version message should it be needed
1833 */
1834- rsrv_version_reply ( prsrv_cast_client );
1835+ rsrv_version_reply ( client );
1836+
1837+ /* these pointers become invalid after signaling casudp_startStopEvent */
1838+ conf = NULL;
1839
1840 epicsEventSignal(casudp_startStopEvent);
1841
1842 while (TRUE) {
1843 status = recvfrom (
1844- IOC_cast_sock,
1845- prsrv_cast_client->recv.buf,
1846- prsrv_cast_client->recv.maxstk,
1847+ recv_sock,
1848+ client->recv.buf,
1849+ client->recv.maxstk,
1850 0,
1851 (struct sockaddr *)&new_recv_addr,
1852 &recv_addr_size);
1853@@ -219,71 +180,82 @@
1854 sockErrBuf);
1855 epicsThreadSleep(1.0);
1856 }
1857+
1858+ } else {
1859+ size_t idx;
1860+ for(idx=0; casIgnoreAddrs[idx]; idx++)
1861+ {
1862+ if(new_recv_addr.sin_addr.s_addr==casIgnoreAddrs[idx]) {
1863+ status = -1; /* ignore */
1864+ break;
1865+ }
1866+ }
1867 }
1868- else if (casudp_ctl == ctlRun) {
1869- prsrv_cast_client->recv.cnt = (unsigned) status;
1870- prsrv_cast_client->recv.stk = 0ul;
1871- epicsTimeGetCurrent(&prsrv_cast_client->time_at_last_recv);
1872-
1873- prsrv_cast_client->minor_version_number = 0;
1874- prsrv_cast_client->seqNoOfReq = 0;
1875+
1876+ if (status >= 0 && casudp_ctl == ctlRun) {
1877+ client->recv.cnt = (unsigned) status;
1878+ client->recv.stk = 0ul;
1879+ epicsTimeGetCurrent(&client->time_at_last_recv);
1880+
1881+ client->minor_version_number = 0;
1882+ client->seqNoOfReq = 0;
1883
1884 /*
1885 * If we are talking to a new client flush to the old one
1886 * in case we are holding UDP messages waiting to
1887 * see if the next message is for this same client.
1888 */
1889- if (prsrv_cast_client->send.stk>sizeof(caHdr)) {
1890- status = memcmp(&prsrv_cast_client->addr,
1891+ if (client->send.stk>sizeof(caHdr)) {
1892+ status = memcmp(&client->addr,
1893 &new_recv_addr, recv_addr_size);
1894 if(status){
1895 /*
1896 * if the address is different
1897 */
1898- cas_send_dg_msg(prsrv_cast_client);
1899- prsrv_cast_client->addr = new_recv_addr;
1900+ cas_send_dg_msg(client);
1901+ client->addr = new_recv_addr;
1902 }
1903 }
1904 else {
1905- prsrv_cast_client->addr = new_recv_addr;
1906+ client->addr = new_recv_addr;
1907 }
1908
1909 if (CASDEBUG>1) {
1910 char buf[40];
1911
1912- ipAddrToDottedIP (&prsrv_cast_client->addr, buf, sizeof(buf));
1913+ ipAddrToDottedIP (&client->addr, buf, sizeof(buf));
1914 errlogPrintf ("CAS: cast server msg of %d bytes from addr %s\n",
1915- prsrv_cast_client->recv.cnt, buf);
1916+ client->recv.cnt, buf);
1917 }
1918
1919 if (CASDEBUG>2)
1920- count = ellCount (&prsrv_cast_client->chanList);
1921+ count = ellCount (&client->chanList);
1922
1923- status = camessage ( prsrv_cast_client );
1924+ status = camessage ( client );
1925 if(status == RSRV_OK){
1926- if(prsrv_cast_client->recv.cnt !=
1927- prsrv_cast_client->recv.stk){
1928+ if(client->recv.cnt !=
1929+ client->recv.stk){
1930 char buf[40];
1931
1932- ipAddrToDottedIP (&prsrv_cast_client->addr, buf, sizeof(buf));
1933+ ipAddrToDottedIP (&client->addr, buf, sizeof(buf));
1934
1935 epicsPrintf ("CAS: partial (damaged?) UDP msg of %d bytes from %s ?\n",
1936- prsrv_cast_client->recv.cnt-prsrv_cast_client->recv.stk, buf);
1937+ client->recv.cnt-client->recv.stk, buf);
1938 }
1939 }
1940 else {
1941 char buf[40];
1942
1943- ipAddrToDottedIP (&prsrv_cast_client->addr, buf, sizeof(buf));
1944+ ipAddrToDottedIP (&client->addr, buf, sizeof(buf));
1945
1946 epicsPrintf ("CAS: invalid (damaged?) UDP request from %s ?\n", buf);
1947 }
1948
1949 if (CASDEBUG>2) {
1950- if ( ellCount (&prsrv_cast_client->chanList) ) {
1951+ if ( ellCount (&client->chanList) ) {
1952 errlogPrintf ("CAS: Fnd %d name matches (%d tot)\n",
1953- ellCount(&prsrv_cast_client->chanList)-count,
1954- ellCount(&prsrv_cast_client->chanList));
1955+ ellCount(&client->chanList)-count,
1956+ ellCount(&client->chanList));
1957 }
1958 }
1959 }
1960@@ -292,15 +264,22 @@
1961 * allow messages to batch up if more are comming
1962 */
1963 nchars = 0; /* supress purify warning */
1964- status = socket_ioctl(IOC_cast_sock, FIONREAD, &nchars);
1965+ status = socket_ioctl(recv_sock, FIONREAD, &nchars);
1966 if (status<0) {
1967 errlogPrintf ("CA cast server: Unable to fetch N characters pending\n");
1968- cas_send_dg_msg (prsrv_cast_client);
1969- clean_addrq ();
1970+ cas_send_dg_msg (client);
1971+ clean_addrq (client);
1972 }
1973 else if (nchars == 0) {
1974- cas_send_dg_msg (prsrv_cast_client);
1975- clean_addrq ();
1976+ cas_send_dg_msg (client);
1977+ clean_addrq (client);
1978 }
1979 }
1980+
1981+ /* ATM never reached, just a placeholder */
1982+
1983+ if(!mysocket)
1984+ client->sock = INVALID_SOCKET; /* only one cast_server should destroy the reply socket */
1985+ destroy_client(client);
1986+ epicsSocketDestroy(recv_sock);
1987 }
1988
1989=== modified file 'src/ioc/rsrv/online_notify.c'
1990--- src/ioc/rsrv/online_notify.c 2014-10-06 05:57:02 +0000
1991+++ src/ioc/rsrv/online_notify.c 2016-02-25 16:34:00 +0000
1992@@ -36,45 +36,18 @@
1993 #include "server.h"
1994
1995 /*
1996- * forcePort ()
1997- */
1998-static void forcePort (ELLLIST *pList, unsigned short port)
1999-{
2000- osiSockAddrNode *pNode;
2001-
2002- pNode = (osiSockAddrNode *) ellFirst ( pList );
2003- while ( pNode ) {
2004- if ( pNode->addr.sa.sa_family == AF_INET ) {
2005- pNode->addr.ia.sin_port = htons ( port );
2006- }
2007- pNode = (osiSockAddrNode *) ellNext ( &pNode->node );
2008- }
2009-}
2010-
2011-/*
2012 * RSRV_ONLINE_NOTIFY_TASK
2013 */
2014 void rsrv_online_notify_task(void *pParm)
2015 {
2016- unsigned priorityOfSelf = epicsThreadGetPrioritySelf ();
2017- osiSockAddrNode *pNode;
2018 double delay;
2019 double maxdelay;
2020 long longStatus;
2021 double maxPeriod;
2022 caHdr msg;
2023 int status;
2024- SOCKET sock;
2025- int intTrue = TRUE;
2026- unsigned short port;
2027 ca_uint32_t beaconCounter = 0;
2028- char * pStr;
2029- int autoBeaconAddr;
2030- ELLLIST autoAddrList;
2031 char buf[16];
2032- unsigned priorityOfUDP;
2033- epicsThreadBooleanStatus tbs;
2034- epicsThreadId tid;
2035
2036 taskwdInsert (epicsThreadGetIdSelf(),NULL,NULL);
2037
2038@@ -94,189 +67,34 @@
2039
2040 delay = 0.02; /* initial beacon period in sec */
2041 maxdelay = maxPeriod;
2042-
2043- /*
2044- * Open the socket.
2045- * Use ARPA Internet address format and datagram socket.
2046- * Format described in <sys/socket.h>.
2047- */
2048- if ( (sock = epicsSocketCreate (AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) {
2049- errlogPrintf ("CAS: online socket creation error\n");
2050- epicsThreadSuspendSelf ();
2051- }
2052-
2053- status = setsockopt (sock, SOL_SOCKET, SO_BROADCAST,
2054- (char *)&intTrue, sizeof(intTrue));
2055- if (status<0) {
2056- errlogPrintf ("CAS: online socket set up error\n");
2057- epicsThreadSuspendSelf ();
2058- }
2059
2060- {
2061- /*
2062- * this connect is to supress a warning message on Linux
2063- * when we shutdown the read side of the socket. If it
2064- * fails (and it will on old ip kernels) we just ignore
2065- * the failure.
2066- */
2067- osiSockAddr sockAddr;
2068- sockAddr.ia.sin_family = AF_UNSPEC;
2069- sockAddr.ia.sin_port = htons ( 0 );
2070- sockAddr.ia.sin_addr.s_addr = htonl (0);
2071- connect ( sock, & sockAddr.sa, sizeof ( sockAddr.sa ) );
2072- shutdown ( sock, SHUT_RD );
2073- }
2074-
2075 memset((char *)&msg, 0, sizeof msg);
2076 msg.m_cmmd = htons (CA_PROTO_RSRV_IS_UP);
2077 msg.m_count = htons (ca_server_port);
2078 msg.m_dataType = htons (CA_MINOR_PROTOCOL_REVISION);
2079-
2080- ellInit ( & beaconAddrList );
2081- ellInit ( & autoAddrList );
2082-
2083- pStr = envGetConfigParam(&EPICS_CAS_AUTO_BEACON_ADDR_LIST, sizeof(buf), buf);
2084- if ( ! pStr ) {
2085- pStr = envGetConfigParam(&EPICS_CA_AUTO_ADDR_LIST, sizeof(buf), buf);
2086- }
2087- if (pStr) {
2088- if (strstr(pStr,"no")||strstr(pStr,"NO")) {
2089- autoBeaconAddr = FALSE;
2090- }
2091- else if (strstr(pStr,"yes")||strstr(pStr,"YES")) {
2092- autoBeaconAddr = TRUE;
2093- }
2094- else {
2095- fprintf(stderr,
2096- "CAS: EPICS_CA(S)_AUTO_ADDR_LIST = \"%s\"? Assuming \"YES\"\n", pStr);
2097- autoBeaconAddr = TRUE;
2098- }
2099- }
2100- else {
2101- autoBeaconAddr = TRUE;
2102- }
2103-
2104- /*
2105- * load user and auto configured
2106- * broadcast address list
2107- */
2108- if (envGetConfigParamPtr(&EPICS_CAS_BEACON_PORT)) {
2109- port = envGetInetPortConfigParam (&EPICS_CAS_BEACON_PORT,
2110- (unsigned short) CA_REPEATER_PORT );
2111- }
2112- else {
2113- port = envGetInetPortConfigParam (&EPICS_CA_REPEATER_PORT,
2114- (unsigned short) CA_REPEATER_PORT );
2115- }
2116-
2117- /*
2118- * discover beacon addresses associated with this interface
2119- */
2120- if ( autoBeaconAddr ) {
2121- osiSockAddr addr;
2122- ELLLIST tmpList;
2123-
2124- ellInit ( &tmpList );
2125- addr.ia.sin_family = AF_UNSPEC;
2126- osiSockDiscoverBroadcastAddresses (&tmpList, sock, &addr);
2127- forcePort ( &tmpList, port );
2128- removeDuplicateAddresses ( &autoAddrList, &tmpList, 1 );
2129- }
2130-
2131- /*
2132- * by default use EPICS_CA_ADDR_LIST for the
2133- * beacon address list
2134- */
2135- {
2136- const ENV_PARAM *pParam;
2137-
2138- if (envGetConfigParamPtr(&EPICS_CAS_INTF_ADDR_LIST) ||
2139- envGetConfigParamPtr(&EPICS_CAS_BEACON_ADDR_LIST)) {
2140- pParam = &EPICS_CAS_BEACON_ADDR_LIST;
2141- }
2142- else {
2143- pParam = &EPICS_CA_ADDR_LIST;
2144- }
2145-
2146- /*
2147- * add in the configured addresses
2148- */
2149- addAddrToChannelAccessAddressList (
2150- &autoAddrList, pParam, port, pParam == &EPICS_CA_ADDR_LIST );
2151- }
2152-
2153- removeDuplicateAddresses ( &beaconAddrList, &autoAddrList, 0 );
2154-
2155- if ( ellCount ( &beaconAddrList ) == 0 ) {
2156- errlogPrintf ("The CA server's beacon address list was empty after initialization?\n");
2157- }
2158-
2159-# ifdef DEBUG
2160- printChannelAccessAddressList (&beaconAddrList);
2161-# endif
2162-
2163- tbs = epicsThreadHighestPriorityLevelBelow ( priorityOfSelf, &priorityOfUDP );
2164- if ( tbs != epicsThreadBooleanStatusSuccess ) {
2165- priorityOfUDP = priorityOfSelf;
2166- }
2167-
2168- casudp_startStopEvent = epicsEventMustCreate(epicsEventEmpty);
2169- casudp_ctl = ctlPause;
2170-
2171- tid = epicsThreadCreate ( "CAS-UDP", priorityOfUDP,
2172- epicsThreadGetStackSize (epicsThreadStackMedium),
2173- cast_server, 0 );
2174- if ( tid == 0 ) {
2175- epicsPrintf ( "CAS: unable to start UDP daemon thread\n" );
2176- }
2177-
2178- epicsEventMustWait(casudp_startStopEvent);
2179+
2180+
2181 epicsEventSignal(beacon_startStopEvent);
2182
2183 while (TRUE) {
2184- pNode = (osiSockAddrNode *) ellFirst (&beaconAddrList);
2185- while (pNode) {
2186- char buf[64];
2187+ ELLNODE *cur;
2188
2189- status = connect (sock, &pNode->addr.sa,
2190- sizeof(pNode->addr.sa));
2191- if (status<0) {
2192+ /* send beacon to each interface */
2193+ for(cur=ellFirst(&beaconAddrList); cur; cur=ellNext(cur))
2194+ {
2195+ osiSockAddrNode *pAddr = CONTAINER(cur, osiSockAddrNode, node);
2196+ status = sendto (beaconSocket, (char *)&msg, sizeof(msg), 0,
2197+ &pAddr->addr.sa, sizeof(pAddr->addr));
2198+ if (status < 0) {
2199 char sockErrBuf[64];
2200- epicsSocketConvertErrnoToString (
2201- sockErrBuf, sizeof ( sockErrBuf ) );
2202- ipAddrToDottedIP (&pNode->addr.ia, buf, sizeof(buf));
2203- errlogPrintf ( "%s: CA beacon routing (connect to \"%s\") error was \"%s\"\n",
2204+ epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
2205+ ipAddrToDottedIP (&pAddr->addr.ia, buf, sizeof(buf));
2206+ errlogPrintf ( "%s: CA beacon (send to \"%s\") error was \"%s\"\n",
2207 __FILE__, buf, sockErrBuf);
2208 }
2209 else {
2210- struct sockaddr_in if_addr;
2211-
2212- osiSocklen_t size = sizeof (if_addr);
2213- status = getsockname (sock, (struct sockaddr *) &if_addr, &size);
2214- if (status<0) {
2215- char sockErrBuf[64];
2216- epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
2217- errlogPrintf ( "%s: CA beacon routing (getsockname) error was \"%s\"\n",
2218- __FILE__, sockErrBuf);
2219- }
2220- else if (if_addr.sin_family==AF_INET) {
2221- msg.m_available = if_addr.sin_addr.s_addr;
2222- msg.m_cid = htonl ( beaconCounter );
2223-
2224- status = send (sock, (char *)&msg, sizeof(msg), 0);
2225- if (status < 0) {
2226- char sockErrBuf[64];
2227- epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
2228- ipAddrToDottedIP (&pNode->addr.ia, buf, sizeof(buf));
2229- errlogPrintf ( "%s: CA beacon (send to \"%s\") error was \"%s\"\n",
2230- __FILE__, buf, sockErrBuf);
2231- }
2232- else {
2233- assert (status == sizeof(msg));
2234- }
2235- }
2236+ assert (status == sizeof(msg));
2237 }
2238- pNode = (osiSockAddrNode *) pNode->node.next;
2239 }
2240
2241 epicsThreadSleep(delay);
2242@@ -287,7 +105,7 @@
2243 }
2244 }
2245
2246- beaconCounter++; /* expected to overflow */
2247+ msg.m_cid = htonl ( beaconCounter++ ); /* expected to overflow */
2248
2249 while (beacon_ctl == ctlPause) {
2250 epicsThreadSleep(0.1);
2251
2252=== modified file 'src/ioc/rsrv/server.h'
2253--- src/ioc/rsrv/server.h 2014-03-06 21:55:13 +0000
2254+++ src/ioc/rsrv/server.h 2016-02-25 16:34:00 +0000
2255@@ -32,6 +32,7 @@
2256 #include "ellLib.h"
2257 #include "epicsTime.h"
2258 #include "epicsAssert.h"
2259+#include "osiSock.h"
2260
2261 #ifdef rsrvRestore_epicsExportSharedSymbols
2262 #define epicsExportSharedSymbols
2263@@ -88,7 +89,7 @@
2264 char *pUserName;
2265 char *pHostName;
2266 epicsEventId blockSem; /* used whenever the client blocks */
2267- SOCKET sock;
2268+ SOCKET sock, udpRecv;
2269 int proto;
2270 epicsThreadId tid;
2271 unsigned minor_version_number;
2272@@ -137,6 +138,16 @@
2273 char modified; /* mod & ev flw ctrl enbl */
2274 };
2275
2276+typedef struct {
2277+ ELLNODE node;
2278+ osiSockAddr tcpAddr, /* TCP listener endpoint */
2279+ udpAddr, /* UDP name unicast receiver endpoint */
2280+ udpbcastAddr; /* UDP name broadcast receiver endpoint */
2281+ SOCKET tcp, udp, udpbcast;
2282+ struct client *client, *bclient;
2283+
2284+ unsigned int startbcast:1;
2285+} rsrv_iface_config;
2286
2287 enum ctl {ctlInit, ctlRun, ctlPause, ctlExit};
2288
2289@@ -160,15 +171,16 @@
2290 #endif
2291
2292 GLBLTYPE int CASDEBUG;
2293-GLBLTYPE SOCKET IOC_sock;
2294-GLBLTYPE SOCKET IOC_cast_sock;
2295-GLBLTYPE unsigned short ca_server_port;
2296-GLBLTYPE ELLLIST clientQ; /* locked by clientQlock */
2297+GLBLTYPE unsigned short ca_server_port, ca_udp_port, ca_beacon_port;
2298+GLBLTYPE ELLLIST clientQ; /* (TCP clients) locked by clientQlock */
2299+GLBLTYPE ELLLIST clientQudp; /* locked by clientQlock */
2300+GLBLTYPE ELLLIST servers; /* rsrv_iface_config::node, read-only after rsrv_init() */
2301 GLBLTYPE ELLLIST beaconAddrList;
2302-GLBLTYPE ELLLIST casIntfAddrList;
2303+GLBLTYPE SOCKET beaconSocket;
2304+GLBLTYPE ELLLIST casIntfAddrList, casMCastAddrList;
2305+GLBLTYPE epicsUInt32 *casIgnoreAddrs;
2306 GLBLTYPE epicsMutexId clientQlock;
2307-GLBLTYPE struct client *prsrv_cast_client;
2308-GLBLTYPE BUCKET *pCaBucket;
2309+GLBLTYPE BUCKET *pCaBucket; /* locked by clientQlock */
2310 GLBLTYPE void *rsrvClientFreeList;
2311 GLBLTYPE void *rsrvChanFreeList;
2312 GLBLTYPE void *rsrvEventFreeList;
2313@@ -176,7 +188,7 @@
2314 GLBLTYPE void *rsrvLargeBufFreeListTCP;
2315 GLBLTYPE unsigned rsrvSizeofLargeBufTCP;
2316 GLBLTYPE void *rsrvPutNotifyFreeList;
2317-GLBLTYPE unsigned rsrvChannelCount;
2318+GLBLTYPE unsigned rsrvChannelCount; /* locked by clientQlock */
2319
2320 GLBLTYPE epicsEventId casudp_startStopEvent;
2321 GLBLTYPE epicsEventId beacon_startStopEvent;
2322@@ -185,6 +197,7 @@
2323 GLBLTYPE volatile enum ctl beacon_ctl;
2324 GLBLTYPE volatile enum ctl castcp_ctl;
2325
2326+GLBLTYPE unsigned int threadPrios[5];
2327
2328 #define CAS_HASH_TABLE_SIZE 4096
2329
2330
2331=== modified file 'src/libCom/bucketLib/bucketLib.c'
2332--- src/libCom/bucketLib/bucketLib.c 2014-08-25 21:27:18 +0000
2333+++ src/libCom/bucketLib/bucketLib.c 2016-02-25 16:34:00 +0000
2334@@ -486,7 +486,7 @@
2335 unsigned count;
2336 unsigned maxEntries;
2337
2338- printf( "Bucket entries in use = %d bytes in use = %ld\n",
2339+ printf( " Bucket entries in use = %d bytes in use = %ld\n",
2340 pb->nInUse,
2341 (long) (sizeof(*pb)+(pb->hashIdMask+1)*
2342 sizeof(ITEM *)+pb->nInUse*sizeof(ITEM)));
2343@@ -511,7 +511,7 @@
2344
2345 mean = X/nElem;
2346 stdDev = sqrt(XX/nElem - mean*mean);
2347- printf( "Bucket entries/hash id - mean = %f std dev = %f max = %d\n",
2348+ printf( " Bucket entries/hash id - mean = %f std dev = %f max = %d\n",
2349 mean,
2350 stdDev,
2351 maxEntries);
2352
2353=== modified file 'src/libCom/env/envDefs.h'
2354--- src/libCom/env/envDefs.h 2010-10-05 19:27:37 +0000
2355+++ src/libCom/env/envDefs.h 2016-02-25 16:34:00 +0000
2356@@ -88,6 +88,8 @@
2357 envGetLongConfigParam(const ENV_PARAM *pParam, long *pLong);
2358 epicsShareFunc unsigned short epicsShareAPI envGetInetPortConfigParam
2359 (const ENV_PARAM *pEnv, unsigned short defaultPort);
2360+epicsShareFunc long epicsShareAPI
2361+ envGetBoolConfigParam(const ENV_PARAM *pParam, int *pBool);
2362 epicsShareFunc long epicsShareAPI epicsPrtEnvParams(void);
2363 epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *value);
2364 epicsShareFunc void epicsShareAPI epicsEnvShow (const char *name);
2365
2366=== modified file 'src/libCom/env/envSubr.c'
2367--- src/libCom/env/envSubr.c 2012-05-03 17:19:34 +0000
2368+++ src/libCom/env/envSubr.c 2016-02-25 16:34:00 +0000
2369@@ -43,6 +43,7 @@
2370 #define epicsExportSharedSymbols
2371 #include "epicsStdlib.h"
2372 #include "epicsStdio.h"
2373+#include "epicsString.h"
2374 #include "errMdef.h"
2375 #include "errlog.h"
2376 #include "envDefs.h"
2377@@ -319,7 +320,19 @@
2378 }
2379 return -1;
2380 }
2381-
2382
2383+
2384+
2385+long epicsShareAPI
2386+envGetBoolConfigParam(const ENV_PARAM *pParam, int *pBool)
2387+{
2388+ char text[20];
2389+
2390+ if(!envGetConfigParam(pParam, sizeof(text), text))
2391+ return -1;
2392+ *pBool = epicsStrCaseCmp(text, "yes")==0;
2393+ return 0;
2394+}
2395+
2396 /*+/subr**********************************************************************
2397 * NAME envPrtConfigParam - print value of a configuration parameter
2398 *
2399
2400=== modified file 'src/libCom/osi/os/Darwin/osdSock.h'
2401--- src/libCom/osi/os/Darwin/osdSock.h 2011-09-09 22:14:35 +0000
2402+++ src/libCom/osi/os/Darwin/osdSock.h 2016-02-25 16:34:00 +0000
2403@@ -41,6 +41,7 @@
2404 #define SOCK_ECONNRESET ECONNRESET
2405 #define SOCK_ETIMEDOUT ETIMEDOUT
2406 #define SOCK_EADDRINUSE EADDRINUSE
2407+#define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL
2408 #define SOCK_ECONNREFUSED ECONNREFUSED
2409 #define SOCK_ECONNABORTED ECONNABORTED
2410 #define SOCK_EINPROGRESS EINPROGRESS
2411
2412=== modified file 'src/libCom/osi/os/Linux/osdSock.h'
2413--- src/libCom/osi/os/Linux/osdSock.h 2014-09-08 22:57:18 +0000
2414+++ src/libCom/osi/os/Linux/osdSock.h 2016-02-25 16:34:00 +0000
2415@@ -43,6 +43,7 @@
2416 #define SOCK_ECONNRESET ECONNRESET
2417 #define SOCK_ETIMEDOUT ETIMEDOUT
2418 #define SOCK_EADDRINUSE EADDRINUSE
2419+#define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL
2420 #define SOCK_ECONNREFUSED ECONNREFUSED
2421 #define SOCK_ECONNABORTED ECONNABORTED
2422 #define SOCK_EINPROGRESS EINPROGRESS
2423
2424=== modified file 'src/libCom/osi/os/RTEMS/osdSock.h'
2425--- src/libCom/osi/os/RTEMS/osdSock.h 2011-09-09 22:14:35 +0000
2426+++ src/libCom/osi/os/RTEMS/osdSock.h 2016-02-25 16:34:00 +0000
2427@@ -51,6 +51,7 @@
2428 #define SOCK_ECONNRESET ECONNRESET
2429 #define SOCK_ETIMEDOUT ETIMEDOUT
2430 #define SOCK_EADDRINUSE EADDRINUSE
2431+#define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL
2432 #define SOCK_ECONNREFUSED ECONNREFUSED
2433 #define SOCK_ECONNABORTED ECONNABORTED
2434 #define SOCK_EINPROGRESS EINPROGRESS
2435
2436=== modified file 'src/libCom/osi/os/WIN32/osdSock.h'
2437--- src/libCom/osi/os/WIN32/osdSock.h 2011-09-09 22:14:35 +0000
2438+++ src/libCom/osi/os/WIN32/osdSock.h 2016-02-25 16:34:00 +0000
2439@@ -21,7 +21,7 @@
2440 # define WIN32
2441 #endif
2442 #include <winsock2.h>
2443-
2444+#include <ws2tcpip.h>
2445
2446 #define SOCKERRNO WSAGetLastError()
2447
2448@@ -49,6 +49,7 @@
2449 #define SOCK_ECONNRESET WSAECONNRESET
2450 #define SOCK_ETIMEDOUT WSAETIMEDOUT
2451 #define SOCK_EADDRINUSE WSAEADDRINUSE
2452+#define SOCK_EADDRNOTAVAIL WSAEADDRNOTAVAIL
2453 #define SOCK_ECONNREFUSED WSAECONNREFUSED
2454 #define SOCK_ECONNABORTED WSAECONNABORTED
2455 #define SOCK_EINPROGRESS WSAEINPROGRESS
2456
2457=== modified file 'src/libCom/osi/os/cygwin32/osdSock.h'
2458--- src/libCom/osi/os/cygwin32/osdSock.h 2015-04-29 19:01:07 +0000
2459+++ src/libCom/osi/os/cygwin32/osdSock.h 2016-02-25 16:34:00 +0000
2460@@ -50,6 +50,7 @@
2461 #define SOCK_ECONNRESET ECONNRESET
2462 #define SOCK_ETIMEDOUT ETIMEDOUT
2463 #define SOCK_EADDRINUSE EADDRINUSE
2464+#define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL
2465 #define SOCK_ECONNREFUSED ECONNREFUSED
2466 #define SOCK_ECONNABORTED ECONNABORTED
2467 #define SOCK_EINPROGRESS EINPROGRESS
2468
2469=== modified file 'src/libCom/osi/os/default/osdNetIntf.c'
2470--- src/libCom/osi/os/default/osdNetIntf.c 2014-06-11 04:37:21 +0000
2471+++ src/libCom/osi/os/default/osdNetIntf.c 2016-02-25 16:34:00 +0000
2472@@ -54,7 +54,7 @@
2473 struct ifreq *ifr;
2474
2475 ifr = ( struct ifreq * )( ifreqSize (pifreq) + ( char * ) pifreq );
2476- ifDepenDebugPrintf( ("ifreqNext() pifreq 0x%08x, size 0x%08x, ifr 0x%08x\n", pifreq, ifreqSize (pifreq), ifr) );
2477+ ifDepenDebugPrintf( ("ifreqNext() pifreq %p, size 0x%x, ifr 0x%p\n", pifreq, (unsigned)ifreqSize (pifreq), ifr) );
2478 return ifr;
2479 }
2480
2481@@ -73,21 +73,6 @@
2482 struct ifreq *pifreq;
2483 struct ifreq *pnextifreq;
2484 osiSockAddrNode *pNewNode;
2485-
2486- if ( pMatchAddr->sa.sa_family == AF_INET ) {
2487- if ( pMatchAddr->ia.sin_addr.s_addr == htonl (INADDR_LOOPBACK) ) {
2488- pNewNode = (osiSockAddrNode *) calloc (1, sizeof (*pNewNode) );
2489- if ( pNewNode == NULL ) {
2490- errlogPrintf ( "osiSockDiscoverBroadcastAddresses(): no memory available for configuration\n" );
2491- return;
2492- }
2493- pNewNode->addr.ia.sin_family = AF_INET;
2494- pNewNode->addr.ia.sin_port = htons ( 0 );
2495- pNewNode->addr.ia.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
2496- ellAdd ( pList, &pNewNode->node );
2497- return;
2498- }
2499- }
2500
2501 /*
2502 * use pool so that we avoid using too much stack space
2503@@ -105,8 +90,7 @@
2504 ifconf.ifc_req = pIfreqList;
2505 status = socket_ioctl (socket, SIOCGIFCONF, &ifconf);
2506 if (status < 0 || ifconf.ifc_len == 0) {
2507- ifDepenDebugPrintf(("osiSockDiscoverBroadcastAddresses(): status: 0x08x, ifconf.ifc_len: %d\n", status, ifconf.ifc_len));
2508- errlogPrintf ("osiSockDiscoverBroadcastAddresses(): unable to fetch network interface configuration\n");
2509+ errlogPrintf ("osiSockDiscoverBroadcastAddresses(): unable to fetch network interface configuration (%d)\n", status);
2510 free (pIfreqList);
2511 return;
2512 }
2513@@ -116,6 +100,7 @@
2514
2515 for ( pifreq = pIfreqList; pifreq <= pIfreqListEnd; pifreq = pnextifreq ) {
2516 uint32_t current_ifreqsize;
2517+ struct sockaddr_in if_addr;
2518
2519 /*
2520 * find the next ifreq
2521@@ -129,8 +114,8 @@
2522
2523 ifDepenDebugPrintf (("osiSockDiscoverBroadcastAddresses(): found IFACE: %s len: 0x%x current_ifreqsize: 0x%x \n",
2524 pIfreqList->ifr_name,
2525- ifreq_size(pifreq),
2526- current_ifreqsize));
2527+ (unsigned)ifreq_size(pifreq),
2528+ (unsigned)current_ifreqsize));
2529
2530 /*
2531 * If its not an internet interface then dont use it
2532@@ -139,6 +124,7 @@
2533 ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): interface \"%s\" was not AF_INET\n", pIfreqList->ifr_name) );
2534 continue;
2535 }
2536+ if_addr = *(struct sockaddr_in *)&pIfreqList->ifr_addr;
2537
2538 /*
2539 * if it isnt a wildcarded interface then look for
2540@@ -149,8 +135,7 @@
2541 continue;
2542 }
2543 if ( pMatchAddr->ia.sin_addr.s_addr != htonl (INADDR_ANY) ) {
2544- struct sockaddr_in *pInetAddr = (struct sockaddr_in *) &pIfreqList->ifr_addr;
2545- if ( pInetAddr->sin_addr.s_addr != pMatchAddr->ia.sin_addr.s_addr ) {
2546+ if ( if_addr.sin_addr.s_addr != pMatchAddr->ia.sin_addr.s_addr ) {
2547 ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" didnt match\n", pIfreqList->ifr_name) );
2548 continue;
2549 }
2550@@ -171,14 +156,6 @@
2551 continue;
2552 }
2553
2554- /*
2555- * dont use the loop back interface
2556- */
2557- if ( pIfreqList->ifr_flags & IFF_LOOPBACK ) {
2558- ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): ignoring loopback interface: \"%s\"\n", pIfreqList->ifr_name) );
2559- continue;
2560- }
2561-
2562 pNewNode = (osiSockAddrNode *) calloc (1, sizeof (*pNewNode) );
2563 if ( pNewNode == NULL ) {
2564 errlogPrintf ( "osiSockDiscoverBroadcastAddresses(): no memory available for configuration\n" );
2565@@ -217,6 +194,22 @@
2566 pNewNode->addr.sa = pIfreqList->ifr_dstaddr;
2567 }
2568 #endif
2569+#if defined(__linux__)
2570+ /* On Linux, even though the 'lo' interface doesn't set IFF_BROADCAST
2571+ * a broadcast route often exists. Assume that sending to 127.255.255.255
2572+ * reaches all local listeners.
2573+ *
2574+ * $ ip route show table local scope link dev lo
2575+ * broadcast 127.0.0.0 proto kernel src 127.0.0.1
2576+ * broadcast 127.255.255.255 proto kernel src 127.0.0.1
2577+ */
2578+ else if ( pIfreqList->ifr_flags & IFF_LOOPBACK && if_addr.sin_addr.s_addr == htonl(INADDR_LOOPBACK) ) {
2579+ memset(&pNewNode->addr.ia, 0, sizeof(pNewNode->addr.ia));
2580+ pNewNode->addr.ia.sin_family = AF_INET;
2581+ pNewNode->addr.ia.sin_addr.s_addr = htonl(0x7fffffff);
2582+ ifDepenDebugPrintf ( ( "assume loopback broadcast addr = %x\n", ntohl ( pNewNode->addr.ia.sin_addr.s_addr ) ) );
2583+ }
2584+#endif
2585 else {
2586 ifDepenDebugPrintf ( ( "osiSockDiscoverBroadcastAddresses(): net intf \"%s\": not point to point or bcast?\n", pIfreqList->ifr_name ) );
2587 free ( pNewNode );
2588
2589=== modified file 'src/libCom/osi/os/freebsd/osdSock.h'
2590--- src/libCom/osi/os/freebsd/osdSock.h 2011-09-09 22:14:35 +0000
2591+++ src/libCom/osi/os/freebsd/osdSock.h 2016-02-25 16:34:00 +0000
2592@@ -44,6 +44,7 @@
2593 #define SOCK_ECONNRESET ECONNRESET
2594 #define SOCK_ETIMEDOUT ETIMEDOUT
2595 #define SOCK_EADDRINUSE EADDRINUSE
2596+#define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL
2597 #define SOCK_ECONNREFUSED ECONNREFUSED
2598 #define SOCK_ECONNABORTED ECONNABORTED
2599 #define SOCK_EINPROGRESS EINPROGRESS
2600
2601=== modified file 'src/libCom/osi/os/iOS/osdSock.h'
2602--- src/libCom/osi/os/iOS/osdSock.h 2011-09-09 22:14:35 +0000
2603+++ src/libCom/osi/os/iOS/osdSock.h 2016-02-25 16:34:00 +0000
2604@@ -42,6 +42,7 @@
2605 #define SOCK_ECONNRESET ECONNRESET
2606 #define SOCK_ETIMEDOUT ETIMEDOUT
2607 #define SOCK_EADDRINUSE EADDRINUSE
2608+#define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL
2609 #define SOCK_ECONNREFUSED ECONNREFUSED
2610 #define SOCK_ECONNABORTED ECONNABORTED
2611 #define SOCK_EINPROGRESS EINPROGRESS
2612
2613=== modified file 'src/libCom/osi/os/solaris/osdSock.h'
2614--- src/libCom/osi/os/solaris/osdSock.h 2011-09-09 22:14:35 +0000
2615+++ src/libCom/osi/os/solaris/osdSock.h 2016-02-25 16:34:00 +0000
2616@@ -52,6 +52,7 @@
2617 #define SOCK_ECONNRESET ECONNRESET
2618 #define SOCK_ETIMEDOUT ETIMEDOUT
2619 #define SOCK_EADDRINUSE EADDRINUSE
2620+#define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL
2621 #define SOCK_ECONNREFUSED ECONNREFUSED
2622 #define SOCK_ECONNABORTED ECONNABORTED
2623 #define SOCK_EINPROGRESS EINPROGRESS
2624
2625=== modified file 'src/libCom/osi/os/vxWorks/osdSock.h'
2626--- src/libCom/osi/os/vxWorks/osdSock.h 2011-09-20 18:07:52 +0000
2627+++ src/libCom/osi/os/vxWorks/osdSock.h 2016-02-25 16:34:00 +0000
2628@@ -73,6 +73,7 @@
2629 #define SOCK_ECONNRESET ECONNRESET
2630 #define SOCK_ETIMEDOUT ETIMEDOUT
2631 #define SOCK_EADDRINUSE EADDRINUSE
2632+#define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL
2633 #define SOCK_ECONNREFUSED ECONNREFUSED
2634 #define SOCK_ECONNABORTED ECONNABORTED
2635 #define SOCK_EINPROGRESS EINPROGRESS
2636
2637=== modified file 'src/std/filters/arr.c'
2638--- src/std/filters/arr.c 2014-09-30 14:19:13 +0000
2639+++ src/std/filters/arr.c 2016-02-25 16:34:00 +0000
2640@@ -182,7 +182,7 @@
2641 static void channel_report(dbChannel *chan, void *pvt, int level, const unsigned short indent)
2642 {
2643 myStruct *my = (myStruct*) pvt;
2644- printf("%*s plugin arr, start=%d, incr=%d, end=%d\n", indent, "",
2645+ printf("%*sArray (arr): start=%d, incr=%d, end=%d\n", indent, "",
2646 my->start, my->incr, my->end);
2647 }
2648
2649
2650=== modified file 'src/std/filters/dbnd.c'
2651--- src/std/filters/dbnd.c 2014-08-28 18:24:48 +0000
2652+++ src/std/filters/dbnd.c 2016-02-25 16:34:00 +0000
2653@@ -102,7 +102,7 @@
2654 static void channel_report(dbChannel *chan, void *pvt, int level, const unsigned short indent)
2655 {
2656 myStruct *my = (myStruct*) pvt;
2657- printf("%*s plugin dbnd, mode=%s, delta=%g%s\n", indent, "",
2658+ printf("%*sDeadband (dbnd): mode=%s, delta=%g%s\n", indent, "",
2659 chfPluginEnumString(modeEnum, my->mode, "n/a"), my->cval,
2660 my->mode == 1 ? "%" : "");
2661 }
2662
2663=== modified file 'src/std/filters/sync.c'
2664--- src/std/filters/sync.c 2014-10-03 19:23:04 +0000
2665+++ src/std/filters/sync.c 2016-02-25 16:34:00 +0000
2666@@ -156,7 +156,7 @@
2667 static void channel_report(dbChannel *chan, void *pvt, int level, const unsigned short indent)
2668 {
2669 myStruct *my = (myStruct*) pvt;
2670- printf("%*s plugin sync, mode=%s, state=%s\n", indent, "",
2671+ printf("%*sSynchronize (sync): mode=%s, state=%s\n", indent, "",
2672 chfPluginEnumString(modeEnum, my->mode, "n/a"), my->state);
2673 }
2674
2675
2676=== modified file 'src/std/filters/ts.c'
2677--- src/std/filters/ts.c 2012-07-17 19:33:31 +0000
2678+++ src/std/filters/ts.c 2016-02-25 16:34:00 +0000
2679@@ -40,7 +40,7 @@
2680
2681 static void channel_report(dbChannel *chan, void *pvt, int level, const unsigned short indent)
2682 {
2683- printf("%*s plugin ts\n", indent, "");
2684+ printf("%*sTimestamp (ts)\n", indent, "");
2685 }
2686
2687 static chfPluginIf pif = {

Subscribers

People subscribed via source and target branches

to all changes: