Files
epics-base/src/ca/windows_depen.c

799 lines
19 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* $Id$
* Author: Jeffrey O. Hill, Chris Timossi
* hill@luke.lanl.gov
* CATimossi@lbl.gov
* (505) 665 1831
* Date: 9-93
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Lawrence Berkley National Laboratory
*
* Modification Log:
* -----------------
* $Log$
* Revision 1.43 1998/09/24 23:52:57 jhill
* eliminated DLL run-down call to ca_task_exit()
*
* Revision 1.42 1998/09/24 21:22:56 jhill
* conn.c
*
* Revision 1.41 1998/07/07 23:01:42 jhill
* use high precision timers
*
* Revision 1.40 1998/06/22 22:32:03 jhill
* EPICS_DLL => EPICS_DLL_NO (so we are backwards compatible
*
* Revision 1.39 1998/06/16 00:41:22 jhill
* consolodated code here into libCom
*
* Revision 1.38 1998/05/29 00:03:21 jhill
* allow CA to run systems w/o local interface query capabilities (ie cygwin32)
*
* Revision 1.37 1998/04/15 21:53:02 jhill
* fixed incomplete init problem
*
* Revision 1.36 1998/04/13 19:14:36 jhill
* fixed task variable problem
*
* Revision 1.35 1998/04/10 23:00:57 jhill
* link with user32 lib under WIN32
*
* Revision 1.34 1998/03/24 20:55:06 jhill
* fixed console title/correct repeater spawn/correct winsock II URL
*
* Revision 1.33 1998/03/12 20:39:11 jhill
* fixed problem where 3.13.beta11 unable to connect to 3.11 with correct native type
*
* Revision 1.32 1998/02/27 01:05:04 jhill
* integrated Timossi's win sock II changes
*
* Revision 1.1.1.3 1996/11/15 17:45:01 timossi
* Interim release from jeff hill
*
* Revision 1.23 1996/11/02 00:51:12 jhill
* many pc port, const in API, and other changes
*
* Revision 1.22 1996/09/16 16:40:13 jhill
* make EPICS version be the console title
*
* Revision 1.21 1996/08/05 19:20:29 jhill
* removed incorrect ver number
*
* Revision 1.20 1995/12/19 19:36:20 jhill
* function prototype changes
*
* Revision 1.19 1995/11/29 19:15:42 jhill
* added $Log$
* added Revision 1.43 1998/09/24 23:52:57 jhill
* added eliminated DLL run-down call to ca_task_exit()
* added
* added Revision 1.42 1998/09/24 21:22:56 jhill
* added conn.c
* added
* added Revision 1.41 1998/07/07 23:01:42 jhill
* added use high precision timers
* added
* added Revision 1.40 1998/06/22 22:32:03 jhill
* added EPICS_DLL => EPICS_DLL_NO (so we are backwards compatible
* added
* added Revision 1.39 1998/06/16 00:41:22 jhill
* added consolodated code here into libCom
* added
* added Revision 1.38 1998/05/29 00:03:21 jhill
* added allow CA to run systems w/o local interface query capabilities (ie cygwin32)
* added
* added Revision 1.37 1998/04/15 21:53:02 jhill
* added fixed incomplete init problem
* added
* added Revision 1.36 1998/04/13 19:14:36 jhill
* added fixed task variable problem
* added
* added Revision 1.35 1998/04/10 23:00:57 jhill
* added link with user32 lib under WIN32
* added
* added Revision 1.34 1998/03/24 20:55:06 jhill
* added fixed console title/correct repeater spawn/correct winsock II URL
* added
* added Revision 1.33 1998/03/12 20:39:11 jhill
* added fixed problem where 3.13.beta11 unable to connect to 3.11 with correct native type
* added
* added Revision 1.32 1998/02/27 01:05:04 jhill
* added integrated Timossi's win sock II changes
* added
* Revision 1.1.1.3 1996/11/15 17:45:01 timossi
* Interim release from jeff hill
*
* added Revision 1.23 1996/11/02 00:51:12 jhill
* added many pc port, const in API, and other changes
* added
* added Revision 1.22 1996/09/16 16:40:13 jhill
* added make EPICS version be the console title
* added
* added Revision 1.21 1996/08/05 19:20:29 jhill
* added removed incorrect ver number
* added
* Revision 1.20 1995/12/19 19:36:20 jhill
* function prototype changes
* to the header
*
*/
#include <math.h>
#ifndef _WIN32
#error This source is specific to WIN32
#endif
/*
* Windows includes
*/
#include <winsock2.h>
#include <ws2tcpip.h>
#include <process.h>
#include "epicsVersion.h"
#include "bsdSocketResource.h"
#include "iocinf.h"
static long offset_time; /* time diff (sec) between 1970 and when windows started */
static LARGE_INTEGER time_prev, time_freq;
/*
* cac_gettimeval
*/
void cac_gettimeval(struct timeval *pt)
{
LARGE_INTEGER time_cur, time_sec, time_remainder;
/*
* dont need to check status since it was checked once
* during initialization to see if the CPU has HR
* counters (Intel and Mips processors do)
*/
QueryPerformanceCounter (&time_cur);
if (time_prev.QuadPart > time_cur.QuadPart) { /* must have been a timer roll-over */
double offset;
/*
* must have been a timer roll-over
* It takes 9.223372036855e+18/time_freq sec
* to roll over this counter (time_freq is 1193182
* sec on my system). This is currently about 245118 years.
*
* attempt to add number of seconds in a 64 bit integer
* in case the timer resolution improves
*/
offset = pow(2.0, 63.0)-1.0/time_freq.QuadPart;
if (offset<=LONG_MAX) {
offset_time += (long) offset;
}
else {
/*
* this problem cant be fixed, but hopefully will never occurr
*/
fprintf (stderr, "%s.%d Timer overflowed\n", __FILE__, __LINE__);
}
}
time_sec.QuadPart = time_cur.QuadPart / time_freq.QuadPart;
time_remainder.QuadPart = time_cur.QuadPart % time_freq.QuadPart;
if (time_sec.QuadPart > LONG_MAX-offset_time) {
/*
* this problem cant be fixed, but hopefully will never occurr
*/
fprintf (stderr, "%s.%d Timer value larger than storage\n", __FILE__, __LINE__);
pt->tv_sec = 0;
pt->tv_usec = 0;
}
else {
/* add time (sec) since 1970 */
pt->tv_sec = offset_time + (long)time_sec.QuadPart;
pt->tv_usec = (long)((time_remainder.QuadPart*1000000)/time_freq.QuadPart);
}
time_prev = time_cur;
}
/*
* cac_block_for_io_completion()
*/
void cac_block_for_io_completion(struct timeval *pTV)
{
cac_mux_io(pTV);
}
/*
* os_specific_sg_io_complete()
*/
void os_specific_sg_io_complete(CASG *pcasg)
{
}
/*
* does nothing but satisfy undefined
*/
void os_specific_sg_create(CASG *pcasg)
{
}
void os_specific_sg_delete(CASG *pcasg)
{
}
void cac_block_for_sg_completion(CASG *pcasg, struct timeval *pTV)
{
cac_mux_io(pTV);
}
/*
* ca_task_initialize()
*/
int epicsShareAPI ca_task_initialize(void)
{
int status;
if (ca_static) {
return ECA_NORMAL;
}
ca_static = (struct CA_STATIC *)
calloc(1, sizeof(*ca_static));
if (!ca_static) {
return ECA_ALLOCMEM;
}
/*
* initialize elapsed time counters
*
* All CPUs running win32 currently have HR
* counters (Intel and Mips processors do)
*/
if (QueryPerformanceCounter (&time_prev)==0) {
return ECA_INTERNAL;
}
if (QueryPerformanceFrequency (&time_freq)==0) {
return ECA_INTERNAL;
}
offset_time = (long)time(NULL) - (long)(time_prev.QuadPart/time_freq.QuadPart);
/*
* this code moved here from dllMain() so that the code will also run
* in object libraries
*/
if (!bsdSockAttach()) {
free (ca_static);
ca_static = NULL;
return ECA_INTERNAL;
}
status = ca_os_independent_init ();
if (status != ECA_NORMAL) {
bsdSockRelease ();
free (ca_static);
ca_static = NULL;
return status;
}
return ECA_NORMAL;
}
/*
* ca_task_exit ()
*
* call this routine if you wish to free resources prior to task
* exit- ca_task_exit() is also executed routinely at task exit.
*/
int epicsShareAPI ca_task_exit (void)
{
if (!ca_static) {
return ECA_NORMAL;
}
ca_process_exit();
free ((char *)ca_static);
ca_static = NULL;
/*
* this code moved here from dllMain() so that the code will also run
* in object libraries
*/
bsdSockRelease ();
return ECA_NORMAL;
}
/*
*
* obtain the local user name
*
* o Indicates failure by setting ptr to nill
*/
char *localUserName()
{
TCHAR tmpStr[256];
DWORD bufsize = sizeof(tmpStr);
char *pTmp;
if (!GetUserName(tmpStr, &bufsize)) {
tmpStr[0] = '\0';
bufsize = 1;
}
pTmp = malloc (bufsize);
if (pTmp!=NULL) {
strncpy(pTmp, tmpStr, bufsize-1);
pTmp[bufsize-1] = '\0';
}
return pTmp;
}
/*
* ca_spawn_repeater()
*/
void ca_spawn_repeater()
{
BOOL status;
char *pImageName = "caRepeater.exe";
STARTUPINFO startupInfo;
PROCESS_INFORMATION processInfo;
//
// This is not called if the repeater is known to be
// already running. (in the event of a race condition
// the 2nd repeater exits when unable to attach to the
// repeater's port)
//
GetStartupInfo (&startupInfo);
startupInfo.lpReserved = NULL;
startupInfo.lpTitle = "EPICS CA Repeater";
startupInfo.dwFlags = STARTF_USESHOWWINDOW;
startupInfo.wShowWindow = SW_SHOWMINNOACTIVE;
status = CreateProcess(
NULL, // pointer to name of executable module (not required if command line is specified)
pImageName, // pointer to command line string
NULL, // pointer to process security attributes
NULL, // pointer to thread security attributes
FALSE, // handle inheritance flag
CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS, // creation flags
NULL, // pointer to new environment block (defaults to caller's environement)
NULL, // pointer to current directory name (defaults to caller's current directory)
&startupInfo, // pointer to STARTUPINFO
&processInfo // pointer to PROCESS_INFORMATION
);
if (status==0) {
DWORD W32status;
LPVOID errStrMsgBuf;
LPVOID complteMsgBuf;
W32status = FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
GetLastError (),
MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &errStrMsgBuf,
0,
NULL
);
if (W32status) {
char *pFmtArgs[] = {
"Failed to start the EPICS CA Repeater -",
pImageName,
errStrMsgBuf,
"Changes may be required in your \"path\" environment variable.",
"PATH = ",
getenv ("path")};
if (pFmtArgs[5]==NULL) {
pFmtArgs[5] = "<empty string>";
}
W32status = FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING |
FORMAT_MESSAGE_ARGUMENT_ARRAY | 80,
"%1 \"%2\". %3 %4 %5 \"%6\"",
0,
MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &complteMsgBuf,
0,
pFmtArgs
);
if (W32status) {
// Display the string.
MessageBox (NULL, complteMsgBuf, "EPICS Channel Access Configuration Problem",
MB_OK|MB_ICONINFORMATION);
LocalFree (complteMsgBuf);
}
else {
// Display the string.
MessageBox (NULL, errStrMsgBuf, "Failed to start EPICS caRepeater.exe",
MB_OK|MB_ICONINFORMATION);
}
// Free the buffer.
LocalFree (errStrMsgBuf);
}
else {
ca_printf ("!!WARNING!!\n");
ca_printf ("Unable to locate the EPICS executable \"%s\".\n", pImageName);
ca_printf ("You may need to modify your environment.\n");
}
}
//
// use of spawn here causes problems when the ca repeater
// inheits open files (and sockets) from the spawning
// process
//
//status = _spawnlp (_P_DETACH, pImageName, pImageName, NULL);
//if (status<0) {
// ca_printf ("!!WARNING!!\n");
// ca_printf ("Unable to locate the EPICS executable \"%s\".\n",
// pImageName);
// ca_printf ("You may need to modify your environment.\n");
//}
}
/*
* caSetDefaultPrintfHandler ()
* use the normal default here
* ( see access.c )
*/
void caSetDefaultPrintfHandler ()
{
ca_static->ca_printf_func = epicsVprintf;
}
/*
*
* Network interface routines
*
*/
/*
* local_addr()
*
* A valid non-loopback local address is required in the
* beacon message in certain situations where
* there are beacon repeaters and there are addresses
* in the EPICS_CA_ADDRESS_LIST for which we dont have
* a strictly correct local server address on a multi-interface
* system. In this situation we use the first valid non-loopback local
* address found in the beacon message.
*/
int local_addr (SOCKET socket, struct sockaddr_in *plcladdr)
{
int status;
INTERFACE_INFO *pIfinfo;
INTERFACE_INFO *pIfinfoList;
struct sockaddr_in *pInetAddr;
unsigned nelem;
DWORD numifs;
DWORD cbBytesReturned;
static struct sockaddr_in addr;
static char init = FALSE;
if (init) {
*plcladdr = addr;
return 0;
}
/*
* nelem is set to the maximum interfaces
* on one machine here
*/
/*
* only valid for winsock 2 and above
*/
if (wsaMajorVersion() < 2 ) {
return -1;
}
nelem = 10;
pIfinfoList = (INTERFACE_INFO *) calloc(nelem, sizeof(INTERFACE_INFO));
if(!pIfinfoList){
return -1;
}
status = WSAIoctl (socket, SIO_GET_INTERFACE_LIST,
NULL, 0,
(LPVOID)pIfinfoList, nelem*sizeof(INTERFACE_INFO),
&cbBytesReturned, NULL, NULL);
if (status != 0 || cbBytesReturned == 0) {
fprintf(stderr, "WSAIoctl failed %d\n",WSAGetLastError());
free(pIfinfoList);
return -1;
}
numifs = cbBytesReturned/sizeof(INTERFACE_INFO);
for (pIfinfo = pIfinfoList; pIfinfo < (pIfinfoList+numifs); pIfinfo++){
/*
* dont use interfaces that have been disabled
*/
if (!(pIfinfo->iiFlags & IFF_UP)) {
continue;
}
/*
* dont use the loop back interface
*/
if (pIfinfo->iiFlags & IFF_LOOPBACK) {
continue;
}
pInetAddr = (struct sockaddr_in *) &pIfinfo->iiAddress;
/*
* If its not an internet inteface
* then dont use it. But for MS Winsock2
* assume 0 means internet.
*/
if (pInetAddr->sin_family != AF_INET) {
if (pInetAddr->sin_family == 0) {
pInetAddr->sin_family = AF_INET;
}
else {
continue;
}
}
/*
* save the interface's IP address
*/
addr = *pInetAddr;
*plcladdr = addr;
init = TRUE;
free (pIfinfoList);
return 0;
}
free (pIfinfoList);
return -1;
}
/*
* caDiscoverInterfaces()
*
* This routine is provided with the address of an ELLLIST, a socket
* a destination port number, and a match address. When the
* routine returns there will be one additional inet address
* (a caAddrNode) in the list for each inet interface found that
* is up and isnt a loop back interface (match addr is INADDR_ANY)
* or it matches the specified interface (match addr isnt INADDR_ANY).
* If the interface supports broadcast then I add its broadcast
* address to the list. If the interface is a point to
* point link then I add the destination address of the point to
* point link to the list. In either case I set the port number
* in the address node to the port supplied in the argument
* list.
*
* LOCK should be applied here for (pList)
* (this is also called from the server)
*/
void epicsShareAPI caDiscoverInterfaces(ELLLIST *pList, SOCKET socket,
unsigned short port, struct in_addr matchAddr)
{
struct sockaddr_in localAddr;
struct sockaddr_in *pInetAddr;
struct sockaddr_in *pInetNetMask;
caAddrNode *pNode;
int status;
INTERFACE_INFO *pIfinfo;
INTERFACE_INFO *pIfinfoList;
unsigned nelem;
int numifs;
DWORD cbBytesReturned;
/*
*
* nelem is set to the maximum interfaces
* on one machine here
*/
/* only valid for winsock 2 and above */
if (wsaMajorVersion() < 2 ) {
fprintf(stderr, "Need to set EPICS_CA_AUTO_ADDR_LIST=NO for winsock 1\n");
return;
}
nelem = 10;
pIfinfoList = (INTERFACE_INFO *) calloc(nelem, sizeof(INTERFACE_INFO));
if(!pIfinfoList){
return;
}
status = WSAIoctl (socket, SIO_GET_INTERFACE_LIST,
NULL, 0,
(LPVOID)pIfinfoList, nelem*sizeof(INTERFACE_INFO),
&cbBytesReturned, NULL, NULL);
if (status != 0 || cbBytesReturned == 0) {
fprintf(stderr, "WSAIoctl failed %d\n",WSAGetLastError());
free(pIfinfoList);
return;
}
numifs = cbBytesReturned/sizeof(INTERFACE_INFO);
for (pIfinfo = pIfinfoList; pIfinfo < (pIfinfoList+numifs); pIfinfo++){
/*
* dont bother with interfaces that have been disabled
*/
if (!(pIfinfo->iiFlags & IFF_UP)) {
continue;
}
/*
* dont use the loop back interface
*/
if (pIfinfo->iiFlags & IFF_LOOPBACK) {
continue;
}
pInetAddr = (struct sockaddr_in *) &pIfinfo->iiAddress;
pInetNetMask = (struct sockaddr_in *) &pIfinfo->iiNetmask;
/*
* If its not an internet inteface
* then dont use it. But for MS Winsock2
* assume 0 means internet.
*/
if (pInetAddr->sin_family != AF_INET) {
if (pInetAddr->sin_family == 0) {
pInetAddr->sin_family = AF_INET;
}
else {
continue;
}
}
/*
* save the interface's IP address
*/
localAddr = *pInetAddr;
/*
* if it isnt a wildcarded interface then look for
* an exact match
*/
if (matchAddr.s_addr != htonl(INADDR_ANY)) {
if (pInetAddr->sin_addr.s_addr != matchAddr.s_addr) {
continue;
}
}
/*
* If this is an interface that supports
* broadcast fetch the broadcast address.
*
* Otherwise if this is a point to point
* interface then use the destination address.
*
* Otherwise CA will not query through the
* interface.
*/
if (pIfinfo->iiFlags & IFF_BROADCAST) {
//pInetAddr = (struct sockaddr_in *)&pIfinfo->iiBroadcastAddress;
pInetAddr->sin_addr.s_addr =
(localAddr.sin_addr.s_addr & pInetNetMask->sin_addr.s_addr) | ~pInetNetMask->sin_addr.s_addr;
}
else if(pIfinfo->iiFlags & IFF_POINTTOPOINT){
//pInetAddr = (struct sockaddr_in *)&pIfinfo->iiBroadcastAddress;
}
else{
continue;
}
pNode = (caAddrNode *) calloc(1,sizeof(*pNode));
if (!pNode) {
continue;
}
pNode->destAddr.in = *pInetAddr;
pNode->destAddr.in.sin_port = htons(port);
pNode->srcAddr.in = localAddr;
/*
* LOCK applied externally
*/
ellAdd (pList, &pNode->node);
}
free(pIfinfoList);
}
#if !defined(EPICS_DLL_NO)
/*
* most of the code here was moved to ca_task_initialize and ca_task_exit()
* so that the code will also run in object libraries
*/
BOOL epicsShareAPI DllMain (HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
{
switch (dwReason) {
case DLL_PROCESS_ATTACH:
# if defined(_DEBUG) && 0
{
char name[256];
char mname[80];
DWORD nchars;
nchars = GetModuleFileName (hModule, mname, 80);
if (!nchars) {
strcpy (mname,"Unknown");
}
sprintf(name,"Process Attach\n\nBuild Date: %s\nBuild Time: %s\n"
"Module Name: %s", __DATE__, __TIME__, mname);
MessageBox (NULL, name, "CA.DLL Version", MB_OK);
}
# endif
# ifdef _DEBUG
fprintf(stderr, "Process attached to ca.dll version %s\n", EPICS_VERSION_STRING);
# endif
break;
case DLL_PROCESS_DETACH:
# ifdef _DEBUG
fprintf(stderr, "Process detached from ca.dll version %s\n", EPICS_VERSION_STRING);
# endif
break;
case DLL_THREAD_ATTACH:
#if _DEBUG
fprintf(stderr, "Thread attached to ca.dll\n");
#endif
break;
case DLL_THREAD_DETACH:
#if _DEBUG
fprintf(stderr, "Thread detached from ca.dll\n");
#endif
break;
default:
break;
}
return TRUE;
}
#endif