mirror of
https://github.com/SDL-Hercules-390/hyperion.git
synced 2026-04-12 15:07:09 +02:00
3386 lines
110 KiB
C
3386 lines
110 KiB
C
/* SHARED.C (c)Copyright Greg Smith, 2002-2012 */
|
|
/* Shared Device Server */
|
|
/* */
|
|
/* Released under "The Q Public License Version 1" */
|
|
/* (http://www.hercules-390.org/herclic.html) as modifications to */
|
|
/* Hercules. */
|
|
|
|
#include "hstdinc.h"
|
|
|
|
DISABLE_GCC_UNUSED_FUNCTION_WARNING;
|
|
|
|
#define _SHARED_C_
|
|
#define _HDASD_DLL_
|
|
|
|
#include "hercules.h"
|
|
#include "opcode.h"
|
|
#include "devtype.h"
|
|
#include "ccwarn.h"
|
|
#include "dasdblks.h"
|
|
|
|
DISABLE_GCC_UNUSED_SET_WARNING;
|
|
|
|
#if defined( OPTION_SHARED_DEVICES )
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Global variables
|
|
*-------------------------------------------------------------------*/
|
|
|
|
DEVHND shared_ckd_device_hndinfo;
|
|
DEVHND shared_fba_device_hndinfo;
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Update notify - called by device handlers for sharable devices
|
|
*-------------------------------------------------------------------*/
|
|
int shared_update_notify (DEVBLK *dev, int block)
|
|
{
|
|
int i, j; /* Indexes */
|
|
|
|
/* Return if no remotes are connected */
|
|
if (dev->shrdconn == 0)
|
|
return 0;
|
|
|
|
for (i = 0; i < SHARED_MAX_SYS; i++)
|
|
{
|
|
|
|
/* Ignore the entry if it doesn't exist or if it's ours
|
|
our if it's already maxed out */
|
|
if (dev->shrd[i] == NULL || dev->shrd[i]->id == dev->shioactive
|
|
|| dev->shrd[i]->purgen < 0)
|
|
continue;
|
|
|
|
/* Check if the block is already entered */
|
|
for (j = 0; j < dev->shrd[i]->purgen; j++)
|
|
if (fetch_fw(dev->shrd[i]->purge[j]) == (U32)block) break;
|
|
|
|
/* Add the block if it's not already there */
|
|
if (j >= dev->shrd[i]->purgen)
|
|
{
|
|
if (dev->shrd[i]->purgen >= SHARED_PURGE_MAX)
|
|
dev->shrd[i]->purgen = -1;
|
|
else
|
|
store_fw (dev->shrd[i]->purge[dev->shrd[i]->purgen++],
|
|
block);
|
|
SHRDTRACE("notify %d added for id=%d, n=%d",
|
|
block, dev->shrd[i]->id, dev->shrd[i]->purgen);
|
|
}
|
|
|
|
} /* for each possible remote system */
|
|
|
|
return 0;
|
|
|
|
} /* shared_update_notify */
|
|
|
|
|
|
/*-------------------------------------------------------------------
|
|
* CKD init exit (client side)
|
|
*-------------------------------------------------------------------*/
|
|
int shared_ckd_init (DEVBLK *dev, int argc, char *argv[] )
|
|
{
|
|
int rc; /* Return code */
|
|
int i; /* Loop index */
|
|
int retry; /* 1=Connection being retried*/
|
|
char *ipname; /* Remote name or address */
|
|
char *port = NULL; /* Remote port */
|
|
char *rmtnum = NULL; /* Remote device number */
|
|
struct hostent *he; /* -> hostent structure */
|
|
char *kw; /* Argument keyword */
|
|
char *op; /* Argument operand */
|
|
BYTE c; /* Used for parsing */
|
|
char *cu = NULL; /* Specified control unit */
|
|
FWORD cyls; /* Remote number cylinders */
|
|
char *p, buf[1024]; /* Work buffer */
|
|
char *strtok_str = NULL; /* last position */
|
|
|
|
/* Process the arguments */
|
|
if (!(retry = dev->connecting))
|
|
{
|
|
dev->connected = 0; /* SHRD_CONNECT not done yet */
|
|
|
|
if (argc < 1 || strlen(argv[0]) >= sizeof(buf))
|
|
return -1;
|
|
STRLCPY( buf, argv[0] );
|
|
|
|
/* First argument is 'ipname:port:devnum' */
|
|
ipname = buf;
|
|
if (strchr(ipname,'/') || strchr(ipname,'\\'))
|
|
return -1;
|
|
p = strchr (buf, ':');
|
|
if (p)
|
|
{
|
|
*p = '\0';
|
|
port = p + 1;
|
|
p = strchr (port, ':');
|
|
}
|
|
if (p)
|
|
{
|
|
*p = '\0';
|
|
rmtnum = p + 1;
|
|
}
|
|
|
|
#if defined( HAVE_SYS_UN_H )
|
|
if ( strcmp (ipname, "localhost") == 0)
|
|
dev->localhost = 1;
|
|
else
|
|
#endif
|
|
{
|
|
if ( (he = gethostbyname (ipname)) == NULL )
|
|
return -1;
|
|
memcpy(&dev->rmtaddr, he->h_addr_list[0], sizeof(dev->rmtaddr));
|
|
}
|
|
|
|
if (port && strlen(port))
|
|
{
|
|
if (sscanf(port, "%hu%c", &dev->rmtport, &c) != 1)
|
|
return -1;
|
|
}
|
|
else
|
|
dev->rmtport = SHARED_DEFAULT_PORT;
|
|
|
|
if (rmtnum && strlen(rmtnum))
|
|
{
|
|
if (strlen (rmtnum) > 4
|
|
|| sscanf (rmtnum, "%hx%c", &dev->rmtnum, &c) != 1)
|
|
return -1;
|
|
}
|
|
else
|
|
dev->rmtnum = dev->devnum;
|
|
|
|
/* Process the remaining arguments */
|
|
for (i = 1; i < argc; i++)
|
|
{
|
|
if (strcasecmp ("readonly", argv[i]) == 0 ||
|
|
strcasecmp ("rdonly", argv[i]) == 0 ||
|
|
strcasecmp ("ro", argv[i]) == 0)
|
|
{
|
|
dev->ckdrdonly = 1;
|
|
continue;
|
|
}
|
|
if (strcasecmp ("fakewrite", argv[i]) == 0 ||
|
|
strcasecmp ("fakewrt", argv[i]) == 0 ||
|
|
strcasecmp ("fw", argv[i]) == 0)
|
|
{
|
|
if (!dev->ckdrdonly)
|
|
{
|
|
// "%1d:%04X Shared: CKD file: 'fakewrite' invalid without 'readonly'"
|
|
WRMSG( HHC00745, "E", LCSS_DEVNUM );
|
|
return -1;
|
|
}
|
|
|
|
dev->ckdfakewr = 1;
|
|
continue;
|
|
}
|
|
if (strlen (argv[i]) > 3
|
|
&& memcmp("cu=", argv[i], 3) == 0)
|
|
{
|
|
kw = strtok_r (argv[i], "=", &strtok_str );
|
|
op = strtok_r (NULL, " \t", &strtok_str );
|
|
cu = op;
|
|
continue;
|
|
}
|
|
#if defined( HAVE_ZLIB )
|
|
if (strlen (argv[i]) > 5
|
|
&& memcmp("comp=", argv[i], 5) == 0)
|
|
{
|
|
kw = strtok_r (argv[i], "=", &strtok_str );
|
|
op = strtok_r (NULL, " \t", &strtok_str );
|
|
dev->rmtcomp = atoi (op);
|
|
if (dev->rmtcomp < 0 || dev->rmtcomp > 9)
|
|
dev->rmtcomp = 0;
|
|
continue;
|
|
}
|
|
#endif
|
|
// "Shared: parameter %s in argument %d is invalid"
|
|
WRMSG( HHC00700, "S", argv[i], i + 1 );
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Set suported compression */
|
|
dev->rmtcomps = 0;
|
|
#if defined( HAVE_ZLIB )
|
|
dev->rmtcomps |= SHRD_LIBZ;
|
|
#endif
|
|
#if defined( CCKD_BZIP2 )
|
|
dev->rmtcomps |= SHRD_BZIP2;
|
|
#endif
|
|
|
|
/* Update the device handler vector */
|
|
dev->hnd = &shared_ckd_device_hndinfo;
|
|
|
|
dev->connecting = 1;
|
|
|
|
init_retry:
|
|
|
|
do {
|
|
rc = clientConnect (dev, retry);
|
|
if (rc < 0)
|
|
{
|
|
// "%1d:%04X Shared: connect pending to file %s"
|
|
WRMSG( HHC00701, "W", LCSS_DEVNUM, dev->filename );
|
|
if (retry) SLEEP(5);
|
|
}
|
|
} while (retry && rc < 0);
|
|
|
|
/* Return if unable to connect */
|
|
if (rc < 0) return 0;
|
|
|
|
dev->ckdnumfd = 1;
|
|
dev->ckdfd[0] = dev->fd;
|
|
|
|
/* Get the number of cylinders */
|
|
rc = clientRequest (dev, cyls, 4, SHRD_QUERY, SHRD_CKDCYLS, NULL, NULL);
|
|
if (rc < 0)
|
|
goto init_retry;
|
|
else if (rc != 4)
|
|
{
|
|
// "%1d:%04X Shared: error retrieving cylinders"
|
|
WRMSG( HHC00702, "S", LCSS_DEVNUM );
|
|
return -1;
|
|
}
|
|
dev->ckdcyls = fetch_fw (cyls);
|
|
|
|
/* Get the device characteristics */
|
|
rc = clientRequest (dev, dev->devchar, sizeof(dev->devchar),
|
|
SHRD_QUERY, SHRD_DEVCHAR, NULL, NULL);
|
|
if (rc < 0)
|
|
goto init_retry;
|
|
else if (rc == 0 || rc > (int)sizeof(dev->devchar))
|
|
{
|
|
// "%1d:%04X Shared: error retrieving device characteristics"
|
|
WRMSG( HHC00703, "S", LCSS_DEVNUM );
|
|
return -1;
|
|
}
|
|
dev->numdevchar = rc;
|
|
|
|
/* Get number of heads from devchar */
|
|
dev->ckdheads = fetch_hw (dev->devchar + 14);
|
|
|
|
/* Calculate number of tracks */
|
|
dev->ckdtrks = dev->ckdcyls * dev->ckdheads;
|
|
dev->ckdhitrk[0] = dev->ckdtrks;
|
|
|
|
/* Check the device type */
|
|
if (dev->devtype == 0)
|
|
dev->devtype = fetch_hw (dev->devchar + 3);
|
|
else if (dev->devtype != fetch_hw (dev->devchar + 3))
|
|
{
|
|
// "%1d:%04X Shared: remote device %04X is a %04X"
|
|
WRMSG( HHC00704, "S", LCSS_DEVNUM, dev->rmtnum, fetch_hw( dev->devchar + 3 ));
|
|
return -1;
|
|
}
|
|
|
|
/* Get the device id */
|
|
rc = clientRequest (dev, dev->devid, sizeof(dev->devid),
|
|
SHRD_QUERY, SHRD_DEVID, NULL, NULL);
|
|
if (rc < 0)
|
|
goto init_retry;
|
|
else if (rc == 0 || rc > (int)sizeof(dev->devid))
|
|
{
|
|
// "%1d:%04X Shared: error retrieving device id"
|
|
WRMSG( HHC00705, "S", LCSS_DEVNUM );
|
|
return -1;
|
|
}
|
|
dev->numdevid = rc;
|
|
|
|
/* Get the serial number if the server supports such a query */
|
|
|
|
if (dev->rmtver < SHARED_VERSION ||
|
|
(dev->rmtver == SHARED_VERSION &&
|
|
dev->rmtrel < SHARED_RELEASE))
|
|
{
|
|
/* Generate a random serial number */
|
|
gen_dasd_serial( dev->serial );
|
|
}
|
|
else /* (server SHOULD support the SHRD_SERIAL query) */
|
|
{
|
|
rc = clientRequest (dev, dev->serial, sizeof(dev->serial),
|
|
SHRD_QUERY, SHRD_SERIAL, NULL, NULL);
|
|
if (rc < 0)
|
|
goto init_retry;
|
|
else if (rc == 0 || rc > (int)sizeof(dev->serial))
|
|
{
|
|
// "%1d:%04X Shared: error retrieving serial number"
|
|
WRMSG( HHC00716, "S", LCSS_DEVNUM );
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Indicate no active track */
|
|
dev->cache = dev->bufcur = -1;
|
|
dev->buf = NULL;
|
|
|
|
/* Set number of sense bytes */
|
|
dev->numsense = 32;
|
|
|
|
/* Locate the CKD dasd table entry */
|
|
dev->ckdtab = dasd_lookup (DASD_CKDDEV, NULL, dev->devtype, dev->ckdcyls);
|
|
if (dev->ckdtab == NULL)
|
|
{
|
|
// "%1d:%04X Shared: device type %4.4X not found in dasd table"
|
|
WRMSG( HHC00706, "S", LCSS_DEVNUM, dev->devtype );
|
|
return -1;
|
|
}
|
|
|
|
/* Set the track size */
|
|
dev->ckdtrksz = (dev->ckdtab->r1 + 511) & ~511;
|
|
|
|
/* Locate the CKD control unit dasd table entry */
|
|
dev->ckdcu = dasd_lookup (DASD_CKDCU, cu ? cu : dev->ckdtab->cu, 0, 0);
|
|
if (dev->ckdcu == NULL)
|
|
{
|
|
// "%1d:%04X Shared: control unit %s not found in dasd table"
|
|
WRMSG( HHC00707, "S", LCSS_DEVNUM, cu ? cu : dev->ckdtab->cu );
|
|
return -1;
|
|
}
|
|
|
|
/* Set flag bit if 3880/3990 controller */
|
|
if (dev->ckdcu->devt == 0x3880)
|
|
dev->ckd3880 = 1;
|
|
if (dev->ckdcu->devt == 0x3990)
|
|
dev->ckd3990 = 1;
|
|
|
|
/* Clear the DPA */
|
|
memset(dev->pgid, 0, sizeof(dev->pgid));
|
|
|
|
/* Request the channel to merge data chained write CCWs into
|
|
a single buffer before passing data to the device handler */
|
|
dev->cdwmerge = 1;
|
|
|
|
/* Purge the cache */
|
|
clientPurge (dev, 0, NULL);
|
|
|
|
/* Log the device geometry */
|
|
if (!dev->batch)
|
|
// "%1d:%04X Shared: file %s: model %s cyls %d heads %d tracks %d trklen %d"
|
|
WRMSG( HHC00708, "I", LCSS_DEVNUM, dev->filename, dev->ckdtab->name,
|
|
dev->ckdcyls, dev->ckdheads, dev->ckdtrks, dev->ckdtrksz );
|
|
|
|
dev->connecting = 0;
|
|
|
|
return 0;
|
|
} /* shared_ckd_init */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* CKD close exit (client side)
|
|
*-------------------------------------------------------------------*/
|
|
static int shared_ckd_close ( DEVBLK *dev )
|
|
{
|
|
/* Purge the cached entries */
|
|
clientPurge (dev, 0, NULL);
|
|
|
|
/* Disconnect and close */
|
|
if (dev->fd >= 0)
|
|
{
|
|
clientRequest (dev, NULL, 0, SHRD_DISCONNECT, 0, NULL, NULL);
|
|
close_socket (dev->fd);
|
|
dev->fd = -1;
|
|
}
|
|
|
|
return 0;
|
|
} /* shared_ckd_close */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* FBA init exit (client side)
|
|
*-------------------------------------------------------------------*/
|
|
int shared_fba_init (DEVBLK *dev, int argc, char *argv[] )
|
|
{
|
|
int rc; /* Return code */
|
|
int i; /* Loop index */
|
|
int retry; /* 1=Connection being retried*/
|
|
char *ipname; /* Remote name or address */
|
|
char *port = NULL; /* Remote port */
|
|
char *rmtnum = NULL; /* Remote device number */
|
|
struct hostent *he; /* -> hostent structure */
|
|
char *kw; /* Argument keyword */
|
|
char *op; /* Argument operand */
|
|
char c; /* Work for sscanf */
|
|
FWORD origin; /* FBA origin */
|
|
FWORD numblks; /* FBA number blocks */
|
|
FWORD blksiz; /* FBA block size */
|
|
char *p, buf[1024]; /* Work buffer */
|
|
#if defined( HAVE_ZLIB )
|
|
char *strtok_str = NULL; /* last token */
|
|
#endif
|
|
|
|
/* Process the arguments */
|
|
if (!(retry = dev->connecting))
|
|
{
|
|
dev->connected = 0; /* SHRD_CONNECT not done yet */
|
|
|
|
kw = op = NULL;
|
|
|
|
if (argc < 1 || strlen(argv[0]) >= sizeof(buf))
|
|
return -1;
|
|
STRLCPY( buf, argv[0] );
|
|
|
|
/* First argument is 'ipname:port:devnum' */
|
|
ipname = buf;
|
|
p = strchr (buf, ':');
|
|
if (p)
|
|
{
|
|
*p = '\0';
|
|
port = p + 1;
|
|
p = strchr (port, ':');
|
|
}
|
|
if (p)
|
|
{
|
|
*p = '\0';
|
|
rmtnum = p + 1;
|
|
}
|
|
|
|
if ( (he = gethostbyname (ipname)) == NULL )
|
|
return -1;
|
|
memcpy(&dev->rmtaddr, he->h_addr_list[0], sizeof(dev->rmtaddr));
|
|
|
|
if (port)
|
|
{
|
|
if (sscanf(port, "%hu%c", &dev->rmtport, &c) != 1)
|
|
return -1;
|
|
}
|
|
else
|
|
dev->rmtport = SHARED_DEFAULT_PORT;
|
|
|
|
if (rmtnum)
|
|
{
|
|
if (strlen (rmtnum) > 4
|
|
|| sscanf (rmtnum, "%hx%c", &dev->rmtnum, &c) != 0)
|
|
return -1;
|
|
}
|
|
else
|
|
dev->rmtnum = dev->devnum;
|
|
|
|
/* Process the remaining arguments */
|
|
rc = 0;
|
|
for (i = 1; i < argc; i++)
|
|
{
|
|
#if defined( HAVE_ZLIB )
|
|
if (strlen (argv[i]) > 5
|
|
&& memcmp("comp=", argv[i], 5) == 0)
|
|
{
|
|
kw = strtok_r (argv[i], "=", &strtok_str );
|
|
op = strtok_r (NULL, " \t", &strtok_str );
|
|
dev->rmtcomp = atoi (op);
|
|
if (dev->rmtcomp < 0 || dev->rmtcomp > 9)
|
|
dev->rmtcomp = 0;
|
|
continue;
|
|
}
|
|
#endif
|
|
// "Shared: parameter %s in argument %d is invalid"
|
|
WRMSG( HHC00700, "S", argv[i], i + 1 );
|
|
rc = -1;
|
|
}
|
|
if (rc)
|
|
return rc;
|
|
}
|
|
|
|
/* Set suported compression */
|
|
dev->rmtcomps = 0;
|
|
#if defined( HAVE_ZLIB )
|
|
dev->rmtcomps |= SHRD_LIBZ;
|
|
#endif
|
|
#if defined( CCKD_BZIP2 )
|
|
dev->rmtcomps |= SHRD_BZIP2;
|
|
#endif
|
|
|
|
/* Update the device handler vector */
|
|
dev->hnd = &shared_fba_device_hndinfo;
|
|
|
|
dev->connecting = 1;
|
|
|
|
init_retry:
|
|
|
|
do {
|
|
rc = clientConnect (dev, retry);
|
|
if (rc < 0)
|
|
{
|
|
// "%1d:%04X Shared: connect pending to file %s"
|
|
WRMSG( HHC00701, "W", LCSS_DEVNUM, dev->filename );
|
|
if (retry) SLEEP(5);
|
|
}
|
|
} while (retry && rc < 0);
|
|
|
|
/* Return if unable to connect */
|
|
if (rc < 0) return 0;
|
|
|
|
/* Get the fba origin */
|
|
rc = clientRequest (dev, origin, 4, SHRD_QUERY, SHRD_FBAORIGIN, NULL, NULL);
|
|
if (rc < 0)
|
|
goto init_retry;
|
|
else if (rc != 4)
|
|
{
|
|
// "%1d:%04X Shared: error retrieving fba origin"
|
|
WRMSG( HHC00709, "S", LCSS_DEVNUM );
|
|
return -1;
|
|
}
|
|
dev->fbaorigin = fetch_fw (origin);
|
|
|
|
/* Get the number of blocks */
|
|
rc = clientRequest (dev, numblks, 4, SHRD_QUERY, SHRD_FBANUMBLK, NULL, NULL);
|
|
if (rc < 0)
|
|
goto init_retry;
|
|
else if (rc != 4)
|
|
{
|
|
// "%1d:%04X Shared: error retrieving fba number blocks"
|
|
WRMSG( HHC00710, "S", LCSS_DEVNUM );
|
|
return -1;
|
|
}
|
|
dev->fbanumblk = fetch_fw (numblks);
|
|
|
|
/* Get the block size */
|
|
rc = clientRequest (dev, blksiz, 4, SHRD_QUERY, SHRD_FBABLKSIZ, NULL, NULL);
|
|
if (rc < 0)
|
|
goto init_retry;
|
|
else if (rc != 4)
|
|
{
|
|
// "%1d:%04X Shared: error retrieving fba block size"
|
|
WRMSG( HHC00711, "S", LCSS_DEVNUM );
|
|
return -1;
|
|
}
|
|
dev->fbablksiz = fetch_fw (blksiz);
|
|
dev->fbaend = (dev->fbaorigin + dev->fbanumblk) * dev->fbablksiz;
|
|
|
|
/* Get the device id */
|
|
rc = clientRequest (dev, dev->devid, sizeof(dev->devid),
|
|
SHRD_QUERY, SHRD_DEVID, NULL, NULL);
|
|
if (rc < 0)
|
|
goto init_retry;
|
|
else if (rc == 0 || rc > (int)sizeof(dev->devid))
|
|
{
|
|
// "%1d:%04X Shared: error retrieving device id"
|
|
WRMSG( HHC00705, "S", LCSS_DEVNUM );
|
|
return -1;
|
|
}
|
|
dev->numdevid = rc;
|
|
|
|
/* Check the device type */
|
|
if (dev->devtype != fetch_hw (dev->devid + 4))
|
|
{
|
|
// "%1d:%04X Shared: remote device %04X is a %04X"
|
|
WRMSG( HHC00704, "S", LCSS_DEVNUM, dev->rmtnum, fetch_hw( dev->devid + 4 ));
|
|
return -1;
|
|
}
|
|
|
|
/* Get the device characteristics */
|
|
rc = clientRequest (dev, dev->devchar, sizeof(dev->devchar),
|
|
SHRD_QUERY, SHRD_DEVCHAR, NULL, NULL);
|
|
if (rc < 0)
|
|
goto init_retry;
|
|
else if (rc == 0 || rc > (int)sizeof(dev->devchar))
|
|
{
|
|
// "%1d:%04X Shared: error retrieving device characteristics"
|
|
WRMSG( HHC00703, "S", LCSS_DEVNUM );
|
|
return -1;
|
|
}
|
|
dev->numdevchar = rc;
|
|
|
|
/* Get the serial number if the server supports such a query */
|
|
|
|
if (dev->rmtver < SHARED_VERSION ||
|
|
(dev->rmtver == SHARED_VERSION &&
|
|
dev->rmtrel < SHARED_RELEASE))
|
|
{
|
|
/* Generate a random serial number */
|
|
gen_dasd_serial( dev->serial );
|
|
}
|
|
else /* (server SHOULD support the SHRD_SERIAL query) */
|
|
{
|
|
rc = clientRequest (dev, dev->serial, sizeof(dev->serial),
|
|
SHRD_QUERY, SHRD_SERIAL, NULL, NULL);
|
|
if (rc < 0)
|
|
goto init_retry;
|
|
else if (rc == 0 || rc > (int)sizeof(dev->serial))
|
|
{
|
|
// "%1d:%04X Shared: error retrieving serial number"
|
|
WRMSG( HHC00716, "S", LCSS_DEVNUM );
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Indicate no active track */
|
|
dev->cache = dev->bufcur = -1;
|
|
dev->buf = NULL;
|
|
|
|
/* Set number of sense bytes */
|
|
dev->numsense = 32;
|
|
|
|
/* Locate the FBA dasd table entry */
|
|
dev->fbatab = dasd_lookup (DASD_FBADEV, NULL, dev->devtype, dev->fbanumblk);
|
|
if (dev->fbatab == NULL)
|
|
{
|
|
// "%1d:%04X Shared: device type %4.4X not found in dasd table"
|
|
WRMSG( HHC00706, "S", LCSS_DEVNUM, dev->devtype );
|
|
return -1;
|
|
}
|
|
|
|
/* Purge the cache */
|
|
clientPurge (dev, 0, NULL);
|
|
|
|
/* Log the device geometry */
|
|
// "%1d:%04X Shared: file %s: model %s origin %"PRId64" blks %d"
|
|
WRMSG( HHC00712, "I", LCSS_DEVNUM, dev->filename, dev->fbatab->name,
|
|
dev->fbaorigin, dev->fbanumblk );
|
|
|
|
dev->connecting = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------
|
|
* FBA close exit (client side)
|
|
*-------------------------------------------------------------------*/
|
|
static int shared_fba_close (DEVBLK *dev)
|
|
{
|
|
/* Purge the cached entries */
|
|
clientPurge (dev, 0, NULL);
|
|
|
|
/* Disconnect and close */
|
|
if (dev->fd >= 0)
|
|
{
|
|
clientRequest (dev, NULL, 0, SHRD_DISCONNECT, 0, NULL, NULL);
|
|
close_socket (dev->fd);
|
|
dev->fd = -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Start I/O exit (client side)
|
|
*-------------------------------------------------------------------*/
|
|
static void shared_start(DEVBLK *dev)
|
|
{
|
|
int rc; /* Return code */
|
|
U16 devnum; /* Cache device number */
|
|
int trk; /* Cache track number */
|
|
int code; /* Response code */
|
|
BYTE buf[SHARED_PURGE_MAX * 4]; /* Purge list */
|
|
|
|
SHRDTRACE("start cur %d cache %d",dev->bufcur,dev->cache);
|
|
|
|
/* Send the START request */
|
|
rc = clientRequest (dev, buf, sizeof(buf),
|
|
SHRD_START, 0, &code, NULL);
|
|
if (rc < 0)
|
|
{
|
|
// "%1d:%04X Shared: error during channel program start"
|
|
WRMSG( HHC00713, "E", LCSS_DEVNUM );
|
|
clientPurge (dev, 0, NULL);
|
|
dev->cache = dev->bufcur = -1;
|
|
dev->buf = NULL;
|
|
return;
|
|
}
|
|
|
|
/* Check for purge */
|
|
if (code & SHRD_PURGE)
|
|
{
|
|
if (rc / 4 > SHARED_PURGE_MAX) rc = 0;
|
|
clientPurge (dev, rc / 4, buf);
|
|
}
|
|
|
|
/* Make previous active entry active again */
|
|
if (dev->cache >= 0)
|
|
{
|
|
cache_lock (CACHE_DEVBUF);
|
|
SHRD_CACHE_GETKEY (dev->cache, devnum, trk);
|
|
if (dev->devnum == devnum && dev->bufcur == trk)
|
|
cache_setflag(CACHE_DEVBUF, dev->cache, ~0, SHRD_CACHE_ACTIVE);
|
|
else
|
|
{
|
|
dev->cache = dev->bufcur = -1;
|
|
dev->buf = NULL;
|
|
}
|
|
cache_unlock (CACHE_DEVBUF);
|
|
}
|
|
} /* shared_start */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* End I/O exit (client side)
|
|
*-------------------------------------------------------------------*/
|
|
static void shared_end (DEVBLK *dev)
|
|
{
|
|
int rc; /* Return code */
|
|
|
|
SHRDTRACE("end cur %d cache %d",dev->bufcur,dev->cache);
|
|
|
|
/* Write the previous active entry if it was updated */
|
|
if (dev->bufupd)
|
|
clientWrite (dev, dev->bufcur);
|
|
dev->bufupd = 0;
|
|
|
|
/* Mark the active entry inactive */
|
|
if (dev->cache >= 0)
|
|
{
|
|
cache_lock (CACHE_DEVBUF);
|
|
cache_setflag (CACHE_DEVBUF, dev->cache, ~SHRD_CACHE_ACTIVE, 0);
|
|
cache_unlock (CACHE_DEVBUF);
|
|
}
|
|
|
|
/* Send the END request */
|
|
rc = clientRequest (dev, NULL, 0, SHRD_END, 0, NULL, NULL);
|
|
if (rc < 0)
|
|
{
|
|
// "%1d:%04X Shared: error during channel program end"
|
|
WRMSG( HHC00714, "E", LCSS_DEVNUM );
|
|
clientPurge (dev, 0, NULL);
|
|
dev->cache = dev->bufcur = -1;
|
|
dev->buf = NULL;
|
|
return;
|
|
}
|
|
} /* shared_end */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Shared ckd read track exit (client side)
|
|
*-------------------------------------------------------------------*/
|
|
static int shared_ckd_read (DEVBLK *dev, int trk, BYTE *unitstat)
|
|
{
|
|
int rc; /* Return code */
|
|
int retries = 10; /* Number read retries */
|
|
int cache; /* Lookup index */
|
|
int lru; /* Available index */
|
|
int len; /* Response length */
|
|
int id; /* Response id */
|
|
int status; /* Response status */
|
|
BYTE *buf; /* Cache buffer */
|
|
BYTE code; /* Response code */
|
|
U16 devnum; /* Response device number */
|
|
BYTE hdr[SHRD_HDR_SIZE + 4]; /* Read request header */
|
|
|
|
/* Return if reading the same track image */
|
|
if (trk == dev->bufcur && dev->cache >= 0)
|
|
{
|
|
dev->bufoff = 0;
|
|
dev->bufoffhi = dev->ckdtrksz;
|
|
return 0;
|
|
}
|
|
|
|
SHRDTRACE( "ckd read trk %d", trk );
|
|
|
|
/* Write the previous active entry if it was updated */
|
|
if (dev->bufupd)
|
|
clientWrite (dev, dev->bufcur);
|
|
dev->bufupd = 0;
|
|
|
|
/* Reset buffer offsets */
|
|
dev->bufoff = 0;
|
|
dev->bufoffhi = dev->ckdtrksz;
|
|
|
|
cache_lock (CACHE_DEVBUF);
|
|
|
|
/* Inactivate the previous image */
|
|
if (dev->cache >= 0)
|
|
cache_setflag (CACHE_DEVBUF, dev->cache, ~SHRD_CACHE_ACTIVE, 0);
|
|
dev->cache = dev->bufcur = -1;
|
|
|
|
cache_retry:
|
|
|
|
/* Lookup the track in the cache */
|
|
cache = cache_lookup (CACHE_DEVBUF, SHRD_CACHE_SETKEY(dev->devnum, trk), &lru);
|
|
|
|
/* Process cache hit */
|
|
if (cache >= 0)
|
|
{
|
|
cache_setflag (CACHE_DEVBUF, cache, ~0, SHRD_CACHE_ACTIVE);
|
|
cache_unlock (CACHE_DEVBUF);
|
|
dev->cachehits++;
|
|
dev->cache = cache;
|
|
dev->buf = cache_getbuf (CACHE_DEVBUF, cache, 0);
|
|
dev->bufcur = trk;
|
|
dev->bufoff = 0;
|
|
dev->bufoffhi = dev->ckdtrksz;
|
|
dev->buflen = shared_ckd_trklen (dev, dev->buf);
|
|
dev->bufsize = cache_getlen (CACHE_DEVBUF, cache);
|
|
SHRDTRACE( "ckd read trk %d cache hit %d", trk, dev->cache );
|
|
return 0;
|
|
}
|
|
|
|
/* Special processing if no available cache entry */
|
|
if (lru < 0)
|
|
{
|
|
SHRDTRACE( "ckd read trk %d cache wait", trk );
|
|
dev->cachewaits++;
|
|
cache_wait (CACHE_DEVBUF);
|
|
goto cache_retry;
|
|
}
|
|
|
|
/* Process cache miss */
|
|
SHRDTRACE( "ckd read trk %d cache miss %d", trk, dev->cache );
|
|
dev->cachemisses++;
|
|
cache_setflag (CACHE_DEVBUF, lru, 0, SHRD_CACHE_ACTIVE|DEVBUF_TYPE_SCKD);
|
|
cache_setkey (CACHE_DEVBUF, lru, SHRD_CACHE_SETKEY(dev->devnum, trk));
|
|
cache_setage (CACHE_DEVBUF, lru);
|
|
buf = cache_getbuf (CACHE_DEVBUF, lru, dev->ckdtrksz);
|
|
|
|
cache_unlock (CACHE_DEVBUF);
|
|
|
|
read_retry:
|
|
|
|
/* Send the read request for the track to the remote host */
|
|
SHRD_SET_HDR (hdr, SHRD_READ, 0, dev->rmtnum, dev->rmtid, 4);
|
|
store_fw (hdr + SHRD_HDR_SIZE, trk);
|
|
rc = clientSend (dev, hdr, NULL, 0);
|
|
if (rc < 0)
|
|
{
|
|
ckd_build_sense (dev, SENSE_EC, 0, 0, FORMAT_1, MESSAGE_0);
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
// "%1d:%04X Shared: remote error reading track %d"
|
|
WRMSG( HHC00715, "E", LCSS_DEVNUM, trk );
|
|
return -1;
|
|
}
|
|
|
|
/* Read the track from the remote host */
|
|
rc = clientRecv (dev, hdr, buf, dev->ckdtrksz);
|
|
SHRD_GET_HDR( hdr, code, status, devnum, id, len );
|
|
if (rc < 0 || code & SHRD_ERROR)
|
|
{
|
|
if (rc < 0 && retries--) goto read_retry;
|
|
ckd_build_sense (dev, SENSE_EC, 0, 0, FORMAT_1, MESSAGE_0);
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
// "%1d:%04X Shared: remote error reading track %d"
|
|
WRMSG( HHC00715, "E", LCSS_DEVNUM, trk );
|
|
return -1;
|
|
}
|
|
|
|
/* Read the sense data if an i/o error occurred */
|
|
if (code & SHRD_IOERR)
|
|
clientRequest (dev, dev->sense, dev->numsense,
|
|
SHRD_SENSE, 0, NULL, NULL);
|
|
|
|
/* Read complete */
|
|
dev->cache = lru;
|
|
dev->buf = cache_getbuf (CACHE_DEVBUF, lru, 0);
|
|
dev->bufcur = trk;
|
|
dev->bufoff = 0;
|
|
dev->bufoffhi = dev->ckdtrksz;
|
|
dev->buflen = shared_ckd_trklen (dev, dev->buf);
|
|
dev->bufsize = cache_getlen (CACHE_DEVBUF, lru);
|
|
dev->buf[0] = 0;
|
|
|
|
return 0;
|
|
} /* shared_ckd_read */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Shared ckd write track exit (client side)
|
|
*-------------------------------------------------------------------*/
|
|
static int shared_ckd_write (DEVBLK *dev, int trk, int off,
|
|
BYTE *buf, int len, BYTE *unitstat)
|
|
{
|
|
int rc; /* Return code */
|
|
|
|
/* Immediately return if fake writing */
|
|
if (dev->ckdfakewr)
|
|
return len;
|
|
|
|
/* Error if opened read-only */
|
|
if (dev->ckdrdonly)
|
|
{
|
|
ckd_build_sense (dev, SENSE_EC, SENSE1_WRI, 0,
|
|
FORMAT_1, MESSAGE_0);
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
return -1;
|
|
}
|
|
|
|
SHRDTRACE( "ckd write trk %d off %d len %d", trk, off, len );
|
|
|
|
/* If the track is not current then read it */
|
|
if (trk != dev->bufcur)
|
|
{
|
|
rc = (dev->hnd->read) (dev, trk, unitstat);
|
|
if (rc < 0)
|
|
{
|
|
dev->bufcur = dev->cache = -1;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Invalid track format if going past buffer end */
|
|
if (off + len > dev->bufoffhi)
|
|
{
|
|
ckd_build_sense (dev, 0, SENSE1_ITF, 0, 0, 0);
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
return -1;
|
|
}
|
|
|
|
/* Copy the data into the buffer */
|
|
if (buf) memcpy (dev->buf + off, buf, len);
|
|
|
|
/* Set low and high updated offsets */
|
|
if (!dev->bufupd || off < dev->bufupdlo)
|
|
dev->bufupdlo = off;
|
|
if (dev->bufoff + len > dev->bufupdhi)
|
|
dev->bufupdhi = off + len;
|
|
|
|
/* Indicate track image has been modified */
|
|
if (!dev->bufupd)
|
|
{
|
|
dev->bufupd = 1;
|
|
shared_update_notify (dev, trk);
|
|
}
|
|
|
|
return len;
|
|
} /* shared_ckd_write */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Return track image length
|
|
*-------------------------------------------------------------------*/
|
|
static int shared_ckd_trklen (DEVBLK *dev, BYTE *buf)
|
|
{
|
|
int sz; /* Size so far */
|
|
|
|
for (sz = CKD_TRKHDR_SIZE;
|
|
memcmp( buf + sz, &CKD_ENDTRK, CKD_ENDTRK_SIZE ) != 0; )
|
|
{
|
|
/* add length of count, key, and data fields */
|
|
sz += CKD_RECHDR_SIZE +
|
|
buf[sz+5] +
|
|
(buf[sz+6] << 8) + buf[sz+7];
|
|
if (sz > dev->ckdtrksz - 8) break;
|
|
}
|
|
|
|
/* add length for end-of-track indicator */
|
|
sz += CKD_RECHDR_SIZE;
|
|
|
|
if (sz > dev->ckdtrksz)
|
|
sz = dev->ckdtrksz;
|
|
|
|
return sz;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Shared usage exit (client side)
|
|
*-------------------------------------------------------------------*/
|
|
static int shared_used (DEVBLK *dev)
|
|
{
|
|
int rc; /* Return code */
|
|
FWORD usage; /* Usage buffer */
|
|
|
|
/* Get usage information */
|
|
rc = clientRequest (dev, usage, 4, SHRD_USED, 0, NULL, NULL);
|
|
if (rc != 4)
|
|
{
|
|
// "%1d:%04X Shared: error retrieving usage information"
|
|
WRMSG( HHC00717, "E", LCSS_DEVNUM );
|
|
return -1;
|
|
}
|
|
return fetch_fw (usage);
|
|
} /* shared_used */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Shared reserve exit (client side)
|
|
*-------------------------------------------------------------------*/
|
|
static void shared_reserve (DEVBLK *dev)
|
|
{
|
|
int rc; /* Return code */
|
|
|
|
/* Issue reserve request */
|
|
rc = clientRequest (dev, NULL, 0, SHRD_RESERVE, 0, NULL, NULL);
|
|
|
|
} /* shared_reserve */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Shared release exit (client side)
|
|
*-------------------------------------------------------------------*/
|
|
static void shared_release (DEVBLK *dev)
|
|
{
|
|
int rc; /* Return code */
|
|
|
|
/* Issue release request */
|
|
rc = clientRequest (dev, NULL, 0, SHRD_RELEASE, 0, NULL, NULL);
|
|
|
|
} /* shared_release */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Write to host
|
|
*
|
|
* NOTE - writes are deferred until a switch occurs or the
|
|
* channel program ends. We are called from either the
|
|
* read exit or the end channel program exit.
|
|
*-------------------------------------------------------------------*/
|
|
static int clientWrite (DEVBLK *dev, int block)
|
|
{
|
|
int rc; /* Return code */
|
|
int retries = 10; /* Number write retries */
|
|
int len; /* Data length */
|
|
BYTE hdr[SHRD_HDR_SIZE + 2 + 4]; /* Write header */
|
|
BYTE code; /* Response code */
|
|
int status; /* Response status */
|
|
int id; /* Response identifier */
|
|
U16 devnum; /* Response device number */
|
|
BYTE errmsg[SHARED_MAX_MSGLEN+1];/* Error message */
|
|
|
|
/* Calculate length to write */
|
|
len = dev->bufupdhi - dev->bufupdlo;
|
|
if (len <= 0 || dev->bufcur < 0)
|
|
{
|
|
dev->bufupdlo = dev->bufupdhi = 0;
|
|
return 0;
|
|
}
|
|
|
|
SHRDTRACE("write rcd %d off %d len %d",block,dev->bufupdlo,len);
|
|
|
|
write_retry:
|
|
|
|
/* The write request contains a 2 byte offset and 4 byte id,
|
|
followed by the data */
|
|
SHRD_SET_HDR (hdr, SHRD_WRITE, 0, dev->rmtnum, dev->rmtid, len + 6);
|
|
store_hw (hdr + SHRD_HDR_SIZE, dev->bufupdlo);
|
|
store_fw (hdr + SHRD_HDR_SIZE + 2, block);
|
|
|
|
rc = clientSend (dev, hdr, dev->buf + dev->bufupdlo, len);
|
|
if (rc < 0)
|
|
{
|
|
// "%1d:%04X Shared: error writing track %d"
|
|
WRMSG( HHC00718, "E", LCSS_DEVNUM, dev->bufcur );
|
|
dev->bufupdlo = dev->bufupdhi = 0;
|
|
clientPurge (dev, 0, NULL);
|
|
return -1;
|
|
}
|
|
|
|
/* Get the response */
|
|
rc = clientRecv (dev, hdr, errmsg, sizeof(errmsg));
|
|
SHRD_GET_HDR (hdr, code, status, devnum, id, len);
|
|
if (rc < 0 || (code & SHRD_ERROR) || (code & SHRD_IOERR))
|
|
{
|
|
if (rc < 0 && retries--) goto write_retry;
|
|
// "%1d:%04X Shared: remote error writing track %d %2.2X-%2.2X"
|
|
WRMSG( HHC00719, "E", LCSS_DEVNUM, dev->bufcur, code, status );
|
|
dev->bufupdlo = dev->bufupdhi = 0;
|
|
clientPurge (dev, 0, NULL);
|
|
return -1;
|
|
}
|
|
|
|
dev->bufupdlo = dev->bufupdhi = 0;
|
|
return rc;
|
|
} /* clientWrite */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Purge cache entries (client side)
|
|
*-------------------------------------------------------------------*/
|
|
static void clientPurge (DEVBLK *dev, int n, void *buf)
|
|
{
|
|
cache_lock(CACHE_DEVBUF);
|
|
dev->rmtpurgen = n;
|
|
dev->rmtpurge = (FWORD *)buf;
|
|
cache_scan (CACHE_DEVBUF, clientPurgescan, dev);
|
|
cache_unlock(CACHE_DEVBUF);
|
|
}
|
|
static int clientPurgescan (int *answer, int ix, int i, void *data)
|
|
{
|
|
U16 devnum; /* Cached device number */
|
|
int trk; /* Cached track */
|
|
int p; /* Purge index */
|
|
DEVBLK *dev = data; /* -> device block */
|
|
|
|
UNREFERENCED(answer);
|
|
SHRD_CACHE_GETKEY(i, devnum, trk);
|
|
if (devnum == dev->devnum)
|
|
{
|
|
if (dev->rmtpurgen == 0) {
|
|
cache_release (ix, i, 0);
|
|
SHRDTRACE("purge %d",trk);
|
|
}
|
|
else
|
|
{
|
|
for (p = 0; p < dev->rmtpurgen; p++)
|
|
{
|
|
if (trk == (int)fetch_fw (dev->rmtpurge[p]))
|
|
{
|
|
SHRDTRACE("purge %d",trk);
|
|
cache_release (ix, i, 0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
} /* clientPurge */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Connect to the server (client side)
|
|
*-------------------------------------------------------------------*/
|
|
static int clientConnect( DEVBLK* dev, int retry )
|
|
{
|
|
int rc; /* Return code */
|
|
struct sockaddr* server; /* -> Server descriptor */
|
|
int len; /* Length server descriptor */
|
|
struct sockaddr_in iserver; /* inet server descriptor */
|
|
#if defined( HAVE_SYS_UN_H )
|
|
struct sockaddr_un userver; /* Unix server descriptor */
|
|
#endif
|
|
int retries = 10; /* Number of retries */
|
|
HWORD id; /* Returned identifier */
|
|
HWORD comp; /* Returned compression parm */
|
|
|
|
SHRDTRACE( "Beg clientConnect sequence for dev %4.4x retry=%d", dev->devnum, retry );
|
|
|
|
do
|
|
{
|
|
/* Close previous connection */
|
|
if (dev->fd >= 0)
|
|
close_socket( dev->fd );
|
|
|
|
/* Get a new socket */
|
|
if (dev->localhost)
|
|
{
|
|
#if defined( HAVE_SYS_UN_H )
|
|
dev->fd = socket( AF_UNIX, SOCK_STREAM, 0 );
|
|
#else
|
|
dev->fd = -1;
|
|
#endif
|
|
dev->ckdfd[0] = dev->fd;
|
|
|
|
if (dev->fd < 0)
|
|
{
|
|
// "%1d:%04X Shared: error in function %s: %s"
|
|
WRMSG( HHC00720, "E", LCSS_DEVNUM, "socket()", strerror( HSO_errno ));
|
|
return -1;
|
|
}
|
|
#if defined( HAVE_SYS_UN_H )
|
|
userver.sun_family = AF_UNIX;
|
|
sprintf( userver.sun_path, "/tmp/hercules_shared.%d", dev->rmtport );
|
|
server = (struct sockaddr*) &userver;
|
|
len = sizeof( userver );
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
dev->fd = socket( AF_INET, SOCK_STREAM, 0 );
|
|
dev->ckdfd[0] = dev->fd;
|
|
|
|
if (dev->fd < 0)
|
|
{
|
|
// "%1d:%04X Shared: error in function %s: %s"
|
|
WRMSG( HHC00720, "E", LCSS_DEVNUM, "socket()", strerror( HSO_errno ));
|
|
return -1;
|
|
}
|
|
|
|
iserver.sin_family = AF_INET;
|
|
iserver.sin_port = htons( dev->rmtport );
|
|
memcpy( &iserver.sin_addr.s_addr, &dev->rmtaddr, sizeof( struct in_addr ));
|
|
server = (struct sockaddr*) &iserver;
|
|
len = sizeof( iserver );
|
|
}
|
|
|
|
/* Connect to the server */
|
|
store_hw( id, dev->rmtid );
|
|
rc = connect( dev->fd, server, len );
|
|
|
|
if (rc < 0)
|
|
SHRDTRACE( "connect rc=%d errno=%d %s", rc, HSO_errno, strerror( HSO_errno ));
|
|
else
|
|
SHRDTRACE( "connect rc=%d", rc );
|
|
|
|
if (rc >= 0)
|
|
{
|
|
/* Request device connection (if we haven't done so yet) */
|
|
if (!dev->connected)
|
|
{
|
|
int flag = (SHARED_VERSION << 4) | SHARED_RELEASE;
|
|
rc = clientRequest( dev, id, 2, SHRD_CONNECT, flag, NULL, &flag );
|
|
if (rc >= 0)
|
|
{
|
|
dev->connected = 1; // (SHRD_CONNECT success)
|
|
dev->rmtid = fetch_hw( id ); // (must only do ONCE!!!)
|
|
dev->rmtver = flag >> 4; // (save server version)
|
|
dev->rmtrel = flag & 0x0f; // (save server release)
|
|
|
|
if (!dev->batch)
|
|
if (MLVL( VERBOSE ))
|
|
// "%1d:%04X Shared: connected to v%d.%d server id %d file %s"
|
|
WRMSG( HHC00721, "I", LCSS_DEVNUM, dev->rmtver,
|
|
dev->rmtrel, dev->rmtid, dev->filename );
|
|
/*
|
|
* Negotiate compression - top 4 bits have the compression
|
|
* algorithms we support (00010000 -> libz; 00100000 ->bzip2,
|
|
* 00110000 -> both) and the bottom 4 bits indicates the
|
|
* libz parm we want to use when sending data back & forth.
|
|
* If the server returns '0' back, then we won't use libz to
|
|
* compress data to the server. What the 'compression
|
|
* algorithms we support' means is that if the data source is
|
|
* cckd or cfba then the server doesn't have to uncompress
|
|
* the data for us if we support the compression algorithm.
|
|
*/
|
|
if (dev->rmtcomp || dev->rmtcomps)
|
|
{
|
|
rc = clientRequest( dev, comp, 2, SHRD_COMPRESS,
|
|
(dev->rmtcomps << 4) | dev->rmtcomp, NULL, NULL );
|
|
if (rc >= 0)
|
|
dev->rmtcomp = fetch_hw( comp );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (!retry)
|
|
// "%1d:%04X Shared: error in connect to file %s: %s"
|
|
WRMSG( HHC00722, "E", LCSS_DEVNUM, dev->filename, strerror( HSO_errno ));
|
|
|
|
if (rc < 0 && retry)
|
|
USLEEP( 20000 ); // (20ms between connect retries?!)
|
|
}
|
|
while (rc < 0 && retry && retries--);
|
|
|
|
SHRDTRACE( "end clientConnect sequence for dev %4.4x retry=%d", dev->devnum, retry );
|
|
|
|
return rc;
|
|
|
|
} /* clientConnect */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Send request to host and get the response
|
|
*
|
|
* No data is sent on the request, buf gets the response.
|
|
* If an uncorrectable connection error occurs -1 is returned.
|
|
* Otherwise *code and *status is set from the response header
|
|
*
|
|
* Since 'buf' may be NULL or not very long, response data is
|
|
* received in a temporary buffer. This enables us to receive
|
|
* an error message from the remote system.
|
|
*-------------------------------------------------------------------*/
|
|
static int clientRequest (DEVBLK *dev, BYTE *buf, int len, int cmd,
|
|
int flags, int *code, int *status)
|
|
{
|
|
int rc; /* Return code */
|
|
int retries = 10; /* Number retries */
|
|
BYTE rcode; /* Request return code */
|
|
BYTE rstatus; /* Request return status */
|
|
U16 rdevnum; /* Request return devnum */
|
|
int rid; /* Request return id */
|
|
int rlen; /* Request return length */
|
|
BYTE hdr[SHRD_HDR_SIZE]; /* Header */
|
|
BYTE temp[256]; /* Temporary buffer */
|
|
|
|
retry:
|
|
|
|
/* Send the request */
|
|
SHRD_SET_HDR(hdr, cmd, flags, dev->rmtnum, dev->rmtid, 0);
|
|
SHRDHDRTRACE( "client request", hdr );
|
|
rc = clientSend (dev, hdr, NULL, 0);
|
|
if (rc < 0) return rc;
|
|
|
|
/* Receive the response */
|
|
rc = clientRecv (dev, hdr, temp, sizeof(temp));
|
|
|
|
/* Retry recv errors */
|
|
if (rc < 0)
|
|
{
|
|
if (1
|
|
&& cmd != SHRD_CONNECT
|
|
&& cmd != SHRD_DISCONNECT
|
|
&& retries--
|
|
)
|
|
{
|
|
SLEEP (1);
|
|
clientConnect (dev, 1);
|
|
goto retry;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* Set code and status */
|
|
SHRD_GET_HDR(hdr, rcode, rstatus, rdevnum, rid, rlen);
|
|
SHRDHDRTRACE( "client response", hdr );
|
|
|
|
if (code) *code = rcode;
|
|
if (status) *status = rstatus;
|
|
|
|
/* Copy the data into the caller's buffer */
|
|
if (buf && len > 0 && rlen > 0)
|
|
memcpy (buf, temp, len < rlen ? len : rlen);
|
|
|
|
return rlen;
|
|
} /* clientRequest */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Send a request to the host
|
|
*
|
|
* 'buf' may be NULL
|
|
* 'buflen' is the length in 'buf' (should be 0 if 'buf' is NULL)
|
|
* 'hdr' may contain additional data; this is detected by the
|
|
* difference between 'buflen' and the length in the header
|
|
*
|
|
* If 'buf' is adjacent to 'hdr' then 'buf' should be NULL
|
|
*
|
|
*-------------------------------------------------------------------*/
|
|
static int clientSend( DEVBLK* dev, BYTE* hdr, BYTE* buf, int buflen )
|
|
{
|
|
int rc; /* Return code */
|
|
BYTE cmd; /* Header command */
|
|
BYTE flag; /* Header flags */
|
|
U16 devnum; /* Header device number */
|
|
int len; /* Header length */
|
|
int id; /* Header identifier */
|
|
int hdrlen; /* Header length + other data*/
|
|
int off; /* Offset to buffer data */
|
|
BYTE* sendbuf; /* Send buffer */
|
|
int sendlen; /* Send length */
|
|
BYTE cbuf[SHRD_HDR_SIZE + 65536]; /* Combined buffer */
|
|
const char* trcmsg; /* Header trace message */
|
|
|
|
/* Save original hdr values */
|
|
SHRD_GET_HDR( hdr, cmd, flag, devnum, id, len );
|
|
|
|
/* Connect to remote device on server if needed */
|
|
if (dev->fd < 0)
|
|
{
|
|
if (SHRD_DISCONNECT == cmd)
|
|
return -1; // (already disconnected!)
|
|
|
|
if ((rc = clientConnect( dev, 1 )) < 0)
|
|
return -1;
|
|
|
|
/* Update values due to clientConnect */
|
|
id = dev->rmtid;
|
|
SHRD_SET_HDR( hdr, cmd, flag, devnum, id, len );
|
|
}
|
|
|
|
/* Make buf, buflen consistent if no additional data to be sent */
|
|
if (buf == NULL) buflen = 0;
|
|
else if (buflen == 0) buf = NULL;
|
|
|
|
/* Calculate length of header, may contain additional data */
|
|
hdrlen = SHRD_HDR_SIZE + (len - buflen);
|
|
off = len - buflen;
|
|
|
|
#if defined( HAVE_ZLIB )
|
|
/* Compress the buf */
|
|
if (1
|
|
&& dev->rmtcomp != 0
|
|
&& flag == 0
|
|
&& off <= SHRD_COMP_MAX_OFF
|
|
&& buflen >= SHARED_COMPRESS_MINLEN
|
|
)
|
|
{
|
|
unsigned long newlen;
|
|
newlen = 65536 - hdrlen;
|
|
memcpy( cbuf, hdr, hdrlen );
|
|
rc = compress2( cbuf + hdrlen, &newlen,
|
|
buf, buflen, dev->rmtcomp );
|
|
if (rc == Z_OK && (int)newlen < buflen)
|
|
{
|
|
cmd |= SHRD_COMP;
|
|
flag = (SHRD_LIBZ << 4) | off;
|
|
hdr = cbuf;
|
|
hdrlen += newlen;
|
|
buf = NULL;
|
|
buflen = 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Combine header and data unless there's no buffer */
|
|
if (buflen == 0)
|
|
{
|
|
sendbuf = hdr;
|
|
sendlen = hdrlen;
|
|
}
|
|
else
|
|
{
|
|
memcpy( cbuf, hdr, hdrlen );
|
|
memcpy( cbuf + hdrlen, buf, buflen );
|
|
sendbuf = cbuf;
|
|
sendlen = hdrlen + buflen;
|
|
}
|
|
|
|
len = (sendlen - SHRD_HDR_SIZE);
|
|
trcmsg = "client send";
|
|
|
|
retry:
|
|
|
|
/* Update hdr with final values */
|
|
SHRD_SET_HDR( sendbuf, cmd, flag, devnum, id, len );
|
|
|
|
/* Trace what we'll be sending */
|
|
if (cmd & SHRD_COMP)
|
|
SHRDHDRTRACE2( trcmsg, sendbuf, "(compressed)" );
|
|
else
|
|
SHRDHDRTRACE( trcmsg, sendbuf );
|
|
|
|
/* Send the header and data */
|
|
if ((rc = send( dev->fd, sendbuf, sendlen, 0 )) < 0)
|
|
{
|
|
/* Trace the socket error */
|
|
SHRDTRACE( "send rc=%d errno=%d %s", rc, HSO_errno, strerror( HSO_errno ));
|
|
|
|
/* If the request we're trying to send to the server
|
|
is a disconnect request, it doesn't make much sense
|
|
to go to the time and effort to re-connect to the
|
|
server only to tell it we're about to DISCONNECT!
|
|
*/
|
|
if (SHRD_DISCONNECT != cmd)
|
|
{
|
|
if ((rc = clientConnect( dev, 0 )) >= 0)
|
|
{
|
|
/* Pickup new rmtid due to clientConnect */
|
|
id = dev->rmtid;
|
|
trcmsg = "client send retry";
|
|
goto retry;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Process return code */
|
|
if (rc < 0 && SHRD_DISCONNECT != cmd)
|
|
{
|
|
// "%1d:%04X Shared: error in send for %2.2X-%2.2X: %s"
|
|
WRMSG( HHC00723, "E", LCSS_DEVNUM, cmd, flag, strerror( HSO_errno ));
|
|
return -1;
|
|
}
|
|
|
|
return rc;
|
|
|
|
} /* clientSend */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Receive a response (client side)
|
|
*-------------------------------------------------------------------*/
|
|
static int clientRecv (DEVBLK *dev, BYTE *hdr, BYTE *buf, int buflen)
|
|
{
|
|
int rc; /* Return code */
|
|
BYTE code; /* Response code */
|
|
BYTE status; /* Response status */
|
|
U16 devnum; /* Response device number */
|
|
int id; /* Response identifier */
|
|
int len; /* Response length */
|
|
static const bool server_req = true;
|
|
|
|
/* Clear the header to zeroes */
|
|
memset( hdr, 0, SHRD_HDR_SIZE );
|
|
|
|
/* Return error if not connected */
|
|
if (dev->fd < 0)
|
|
{
|
|
// "%1d:%04X Shared: not connected to file %s"
|
|
WRMSG( HHC00724, "E", LCSS_DEVNUM, dev->filename );
|
|
return -1;
|
|
}
|
|
|
|
/* Receive the header */
|
|
rc = recvData (dev->fd, hdr, buf, buflen, !server_req );
|
|
if (rc < 0)
|
|
{
|
|
if (rc != -1 && rc != -HSO_ECONNABORTED)
|
|
// "%1d:%04X Shared: error in receive: %s"
|
|
WRMSG( HHC00725, "E", LCSS_DEVNUM, strerror( -rc ));
|
|
return rc;
|
|
}
|
|
|
|
SHRD_GET_HDR(hdr, code, status, devnum, id, len);
|
|
SHRDHDRTRACE( "client recv", hdr );
|
|
|
|
/* Handle remote logical error */
|
|
if (code & SHRD_ERROR)
|
|
{
|
|
// "%1d:%04X Shared: remote error %2.2X-%2.2X: %s"
|
|
WRMSG( HHC00726, "E", LCSS_DEVNUM, code, status, buf );
|
|
len = 0;
|
|
}
|
|
|
|
/* Reset code/status if response was compressed */
|
|
if (len > 0 && code == SHRD_COMP)
|
|
{
|
|
code = SHRD_OK;
|
|
status = 0;
|
|
}
|
|
|
|
/* Reset the header */
|
|
SHRD_SET_HDR(hdr, code, status, devnum, id, len);
|
|
|
|
return len;
|
|
} /* clientRecv */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Receive data (server or client)
|
|
*-------------------------------------------------------------------
|
|
*
|
|
* Returns:
|
|
*
|
|
* > 0 number of bytes received
|
|
*
|
|
* 0 no data available
|
|
*
|
|
* -1 data decompression error
|
|
*
|
|
* < 1 a socket error has occurred; the return code is the
|
|
* negative of the socket error code (e.g. -ECONNRESET),
|
|
* so you should use "strerror( -rc )" to report it.
|
|
*
|
|
*-------------------------------------------------------------------*/
|
|
static int recvData(int sock, BYTE *hdr, BYTE *buf, int buflen, bool server_req)
|
|
{
|
|
int rc; /* Return code */
|
|
int rlen; /* Data length to recv */
|
|
int recvlen; /* Total length */
|
|
BYTE *recvbuf; /* Receive buffer */
|
|
BYTE cmd; /* Header command */
|
|
BYTE flag; /* Header flags */
|
|
U16 devnum; /* Header device number */
|
|
int id; /* Header identifier */
|
|
int len; /* Header length */
|
|
int comp = 0; /* Compression type */
|
|
int off = 0; /* Offset to compressed data */
|
|
DEVBLK *dev = NULL; /* For 'SHRDTRACE' */
|
|
BYTE cbuf[65536]; /* Compressed buffer */
|
|
|
|
/* Receive the header */
|
|
for (recvlen = 0; recvlen < (int)SHRD_HDR_SIZE; recvlen += rc)
|
|
{
|
|
rc = recv (sock, hdr + recvlen, SHRD_HDR_SIZE - recvlen, 0);
|
|
if (rc < 0)
|
|
return -HSO_errno;
|
|
else if (rc == 0)
|
|
return -HSO_ECONNABORTED;
|
|
}
|
|
|
|
SHRD_GET_HDR (hdr, cmd, flag, devnum, id, len);
|
|
SHRDHDRTRACE( "recvData", hdr );
|
|
|
|
/* Return if no data */
|
|
if (len == 0) return 0;
|
|
|
|
/* Check for compressed data */
|
|
if ((server_req && (cmd & SHRD_COMP))
|
|
|| (!server_req && cmd == SHRD_COMP))
|
|
{
|
|
comp = (flag & SHRD_COMP_MASK) >> 4;
|
|
off = flag & SHRD_COMP_OFF;
|
|
cmd &= ~SHRD_COMP;
|
|
flag = 0;
|
|
recvbuf = cbuf;
|
|
rlen = len;
|
|
}
|
|
else
|
|
{
|
|
recvbuf = buf;
|
|
rlen = buflen < len ? buflen : len;
|
|
}
|
|
|
|
/* Receive the data */
|
|
for (recvlen = 0; recvlen < rlen; recvlen += rc)
|
|
{
|
|
rc = recv (sock, recvbuf + recvlen, len - recvlen, 0);
|
|
if (rc < 0)
|
|
return -HSO_errno;
|
|
else if (rc == 0)
|
|
return -HSO_ECONNABORTED;
|
|
}
|
|
|
|
/* Flush any remaining data */
|
|
for (; rlen < len; rlen += rc)
|
|
{
|
|
BYTE buf[256];
|
|
rc = recv (sock, buf, len - rlen < 256 ? len - rlen : 256, 0);
|
|
if (rc < 0)
|
|
return -HSO_errno;
|
|
else if (rc == 0)
|
|
return -HSO_ECONNABORTED;
|
|
}
|
|
|
|
/* Check for compression */
|
|
if (comp == SHRD_LIBZ) {
|
|
#if defined( HAVE_ZLIB )
|
|
unsigned long newlen;
|
|
|
|
if (off > 0)
|
|
memcpy (buf, cbuf, off);
|
|
newlen = buflen - off;
|
|
rc = uncompress(buf + off, &newlen, cbuf + off, len - off);
|
|
if (rc == Z_OK)
|
|
recvlen = (int)newlen + off;
|
|
else
|
|
{
|
|
// "Shared: decompress error %d offset %d length %d"
|
|
WRMSG( HHC00727, "E", rc, off, len - off );
|
|
recvlen = -1;
|
|
}
|
|
#else
|
|
// "Shared: data compressed using method %s is unsupported"
|
|
WRMSG( HHC00728, "E", "libz" );
|
|
recvlen = -1;
|
|
#endif
|
|
}
|
|
else if (comp == SHRD_BZIP2)
|
|
{
|
|
#if defined( CCKD_BZIP2 )
|
|
unsigned int newlen;
|
|
|
|
if (off > 0)
|
|
memcpy (buf, cbuf, off);
|
|
|
|
newlen = buflen - off;
|
|
rc = BZ2_bzBuffToBuffDecompress((void *)(buf + off), &newlen, (void *)(cbuf + off), len - off, 0, 0);
|
|
if (rc == BZ_OK)
|
|
recvlen = (int)newlen + off;
|
|
else
|
|
{
|
|
// "Shared: decompress error %d offset %d length %d"
|
|
WRMSG( HHC00727, "E", rc, off, len - off );
|
|
recvlen = -1;
|
|
}
|
|
#else
|
|
// "Shared: data compressed using method %s is unsupported"
|
|
WRMSG( HHC00728, "E", "bzip2" );
|
|
recvlen = -1;
|
|
#endif
|
|
}
|
|
|
|
if (recvlen > 0)
|
|
{
|
|
SHRD_SET_HDR (hdr, cmd, flag, devnum, id, recvlen);
|
|
if (comp)
|
|
SHRDHDRTRACE2( "recvData", hdr, "(uncompressed)" );
|
|
}
|
|
|
|
return recvlen;
|
|
|
|
} /* recvData */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Convert shared command code to string for tracing purposes
|
|
*-------------------------------------------------------------------*/
|
|
static const char* shrdcmd2str( const BYTE cmd )
|
|
{
|
|
switch (cmd)
|
|
{
|
|
// Requests
|
|
case SHRD_CONNECT: return "CONNECT ";
|
|
case SHRD_DISCONNECT: return "DISCONNE";
|
|
case SHRD_START: return "START ";
|
|
case SHRD_END: return "END ";
|
|
case SHRD_RESUME: return "RESUME ";
|
|
case SHRD_SUSPEND: return "SUSPEND ";
|
|
case SHRD_RESERVE: return "RESERVE ";
|
|
case SHRD_RELEASE: return "RELEASE ";
|
|
case SHRD_READ: return "READ ";
|
|
case SHRD_WRITE: return "WRITE ";
|
|
case SHRD_SENSE: return "SENSE ";
|
|
case SHRD_QUERY: return "QUERY ";
|
|
case SHRD_COMPRESS: return "COMPRESS";
|
|
|
|
// Response codes
|
|
case SHRD_OK: return "OK ";
|
|
case SHRD_ERROR: return "ERROR ";
|
|
case SHRD_IOERR: return "IOERR ";
|
|
case SHRD_BUSY: return "BUSY ";
|
|
case SHRD_COMP: return "COMP ";
|
|
case SHRD_PURGE: return "PURGE ";
|
|
|
|
// Error responses
|
|
case SHRD_ERROR_INVALID: return "INVALID ";
|
|
case SHRD_ERROR_BADVERS: return "BADVERS ";
|
|
case SHRD_ERROR_NOTINIT: return "NOTINIT ";
|
|
case SHRD_ERROR_NOTCONN: return "NOTCONN ";
|
|
case SHRD_ERROR_NOTAVAIL: return "NOTAVAIL";
|
|
case SHRD_ERROR_NOMEM: return "NOMEM ";
|
|
case SHRD_ERROR_NOTACTIVE: return "NOTACTIV";
|
|
case SHRD_ERROR_NODEVICE: return "NODEVICE";
|
|
case SHRD_ERROR_CONNECTED: return "ECONNECT";
|
|
|
|
default: return "????????";
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Process a request (server side)
|
|
*-------------------------------------------------------------------*/
|
|
static void serverRequest (DEVBLK *dev, int ix, BYTE *hdr, BYTE *buf)
|
|
{
|
|
int rc; /* Return code */
|
|
int i; /* Loop index */
|
|
BYTE cmd; /* Header command */
|
|
BYTE flag; /* Header flags */
|
|
U16 devnum; /* Header device number */
|
|
int id; /* Header identifier */
|
|
int len; /* Header length */
|
|
int code; /* Response code */
|
|
int rcd; /* Record to read/write */
|
|
int off; /* Offset into record */
|
|
char trcmsg[32];
|
|
|
|
/* Extract header information */
|
|
SHRD_GET_HDR (hdr, cmd, flag, devnum, id, len);
|
|
MSGBUF( trcmsg, "server request [%d]", ix );
|
|
SHRDHDRTRACE( trcmsg, hdr );
|
|
|
|
/* Save time of last request (for connection timeout purposes) */
|
|
dev->shrd[ix]->time = time( NULL );
|
|
|
|
/* Process the request */
|
|
switch (cmd) {
|
|
|
|
case SHRD_CONNECT:
|
|
if (dev->connecting)
|
|
{
|
|
serverError (dev, ix, SHRD_ERROR_NOTINIT, cmd,
|
|
"device not initialized");
|
|
break;
|
|
}
|
|
if ((flag >> 4) != SHARED_VERSION)
|
|
{
|
|
serverError (dev, ix, SHRD_ERROR_BADVERS, cmd,
|
|
"shared version mismatch");
|
|
break;
|
|
}
|
|
dev->shrd[ix]->release = flag & 0x0f;
|
|
SHRD_SET_HDR (hdr, 0, (SHARED_VERSION << 4) | SHARED_RELEASE, dev->devnum, id, 2);
|
|
store_hw (buf, id);
|
|
serverSend (dev, ix, hdr, buf, 2);
|
|
break;
|
|
|
|
case SHRD_DISCONNECT:
|
|
SHRD_SET_HDR (hdr, 0, 0, dev->devnum, id, 0);
|
|
serverSend (dev, ix, hdr, NULL, 0);
|
|
dev->shrd[ix]->disconnect = 1;
|
|
|
|
OBTAIN_DEVLOCK( dev );
|
|
{
|
|
/* Make the device available if this system active on it */
|
|
if (dev->shioactive == id)
|
|
{
|
|
if (!dev->suspended)
|
|
{
|
|
dev->busy = 0;
|
|
dev->shioactive = DEV_SYS_NONE;
|
|
}
|
|
else
|
|
dev->shioactive = DEV_SYS_LOCAL;
|
|
if (dev->shiowaiters)
|
|
signal_condition (&dev->shiocond);
|
|
}
|
|
}
|
|
RELEASE_DEVLOCK( dev );
|
|
break;
|
|
|
|
case SHRD_START:
|
|
case SHRD_RESUME:
|
|
|
|
OBTAIN_DEVLOCK( dev );
|
|
{
|
|
/* If the device is suspended locally then grab it */
|
|
if (dev->shioactive == DEV_SYS_LOCAL && dev->suspended && !dev->reserved)
|
|
dev->shioactive = id;
|
|
|
|
/* Check if the device is busy */
|
|
if (dev->shioactive != id && dev->shioactive != DEV_SYS_NONE)
|
|
{
|
|
SHRDTRACE( "server request busy id=%d shioactive=%d reserved=%d",
|
|
id, dev->shioactive, dev->reserved );
|
|
/* If the 'nowait' bit is on then respond 'busy' */
|
|
if (flag & SHRD_NOWAIT)
|
|
{
|
|
RELEASE_DEVLOCK( dev );
|
|
SHRD_SET_HDR (hdr, SHRD_BUSY, 0, dev->devnum, id, 0);
|
|
serverSend (dev, ix, hdr, NULL, 0);
|
|
break;
|
|
}
|
|
|
|
dev->shrd[ix]->waiting = 1;
|
|
|
|
/* Wait while the device is busy by the local system */
|
|
while (dev->shioactive == DEV_SYS_LOCAL && !dev->suspended)
|
|
{
|
|
dev->shiowaiters++;
|
|
wait_condition (&dev->shiocond, &dev->lock);
|
|
dev->shiowaiters--;
|
|
}
|
|
|
|
/* Return with the 'waiting' bit on if busy by a remote system */
|
|
if (dev->shioactive != DEV_SYS_NONE && dev->shioactive != DEV_SYS_LOCAL)
|
|
{
|
|
RELEASE_DEVLOCK( dev );
|
|
break;
|
|
}
|
|
|
|
dev->shrd[ix]->waiting = 0;
|
|
}
|
|
|
|
/* Make this system active on the device */
|
|
dev->shioactive = id;
|
|
dev->busy = 1;
|
|
sysblk.shrdcount++;
|
|
SHRDTRACE( "server request active id=%d", id );
|
|
|
|
/* Increment excp count */
|
|
dev->excps++;
|
|
}
|
|
RELEASE_DEVLOCK( dev );
|
|
|
|
/* Call the i/o start or resume exit */
|
|
if (cmd == SHRD_START && dev->hnd->start)
|
|
(dev->hnd->start) (dev);
|
|
else if (cmd == SHRD_RESUME && dev->hnd->resume)
|
|
(dev->hnd->resume) (dev);
|
|
|
|
/* Get the purge list */
|
|
if (dev->shrd[ix]->purgen == 0)
|
|
code = len = 0;
|
|
else
|
|
{
|
|
code = SHRD_PURGE;
|
|
if (dev->shrd[ix]->purgen < 0)
|
|
len = 0;
|
|
else
|
|
len = 4 * dev->shrd[ix]->purgen;
|
|
}
|
|
|
|
/* Send the response */
|
|
SHRD_SET_HDR (hdr, code, 0, dev->devnum, id, len);
|
|
rc = serverSend (dev, ix, hdr, (BYTE *)dev->shrd[ix]->purge, len);
|
|
if (rc >= 0)
|
|
dev->shrd[ix]->purgen = 0;
|
|
break;
|
|
|
|
case SHRD_END:
|
|
case SHRD_SUSPEND:
|
|
/* Must be active on the device for this command */
|
|
if (dev->shioactive != id)
|
|
{
|
|
serverError (dev, ix, SHRD_ERROR_NOTACTIVE, cmd,
|
|
"not active on this device");
|
|
break;
|
|
}
|
|
|
|
/* Call the I/O end/suspend exit */
|
|
if (cmd == SHRD_END && dev->hnd->end)
|
|
(dev->hnd->end) (dev);
|
|
else if (cmd == SHRD_SUSPEND && dev->hnd->suspend)
|
|
(dev->hnd->suspend) (dev);
|
|
|
|
OBTAIN_DEVLOCK( dev );
|
|
{
|
|
/* Make the device available if it's not reserved */
|
|
if (!dev->reserved)
|
|
{
|
|
/* If locally suspended then return the device to local */
|
|
if (dev->suspended)
|
|
{
|
|
dev->shioactive = DEV_SYS_LOCAL;
|
|
dev->busy = 1;
|
|
}
|
|
else
|
|
{
|
|
dev->shioactive = DEV_SYS_NONE;
|
|
dev->busy = 0;
|
|
}
|
|
|
|
/* Reset any 'waiting' bits */
|
|
for (i = 0; i < SHARED_MAX_SYS; i++)
|
|
if (dev->shrd[i])
|
|
dev->shrd[i]->waiting = 0;
|
|
|
|
/* Notify any waiters */
|
|
if (dev->shiowaiters)
|
|
signal_condition (&dev->shiocond);
|
|
}
|
|
SHRDTRACE( "server request inactive id=%d", id );
|
|
}
|
|
RELEASE_DEVLOCK( dev );
|
|
|
|
/* Send response back */
|
|
SHRD_SET_HDR (hdr, 0, 0, dev->devnum, id, 0);
|
|
serverSend (dev, ix, hdr, NULL, 0);
|
|
break;
|
|
|
|
case SHRD_RESERVE:
|
|
/* Must be active on the device for this command */
|
|
if (dev->shioactive != id)
|
|
{
|
|
serverError (dev, ix, SHRD_ERROR_NOTACTIVE, cmd,
|
|
"not active on this device");
|
|
break;
|
|
}
|
|
|
|
OBTAIN_DEVLOCK( dev );
|
|
{
|
|
dev->reserved = 1;
|
|
}
|
|
RELEASE_DEVLOCK( dev );
|
|
|
|
SHRDTRACE( "server request reserved id=%d", id );
|
|
|
|
/* Call the I/O reserve exit */
|
|
if (dev->hnd->reserve) (dev->hnd->reserve) (dev);
|
|
|
|
/* Send response back */
|
|
SHRD_SET_HDR (hdr, 0, 0, dev->devnum, id, 0);
|
|
serverSend (dev, ix, hdr, NULL, 0);
|
|
|
|
break;
|
|
|
|
case SHRD_RELEASE:
|
|
/* Must be active on the device for this command */
|
|
if (dev->shioactive != id)
|
|
{
|
|
serverError (dev, ix, SHRD_ERROR_NOTACTIVE, cmd,
|
|
"not active on this device");
|
|
break;
|
|
}
|
|
|
|
/* Call the I/O release exit */
|
|
if (dev->hnd->release) (dev->hnd->release) (dev);
|
|
|
|
OBTAIN_DEVLOCK( dev );
|
|
{
|
|
dev->reserved = 0;
|
|
}
|
|
RELEASE_DEVLOCK( dev );
|
|
|
|
SHRDTRACE( "server request released id=%d", id );
|
|
|
|
/* Send response back */
|
|
SHRD_SET_HDR (hdr, 0, 0, dev->devnum, id, 0);
|
|
serverSend (dev, ix, hdr, NULL, 0);
|
|
|
|
break;
|
|
|
|
case SHRD_READ:
|
|
/* Must be active on the device for this command */
|
|
if (dev->shioactive != id)
|
|
{
|
|
serverError (dev, ix, SHRD_ERROR_NOTACTIVE, cmd,
|
|
"not active on this device");
|
|
break;
|
|
}
|
|
|
|
/* Set the compressions client is willing to accept */
|
|
dev->comps = dev->shrd[ix]->comps;
|
|
dev->comp = dev->compoff = 0;
|
|
|
|
/* Call the I/O read exit */
|
|
rcd = (int)fetch_fw (buf);
|
|
rc = (dev->hnd->read) (dev, rcd, &flag);
|
|
SHRDTRACE( "server request read rcd %d flag %2.2x rc=%d",
|
|
rcd, flag, rc );
|
|
|
|
if (rc < 0)
|
|
code = SHRD_IOERR;
|
|
else
|
|
{
|
|
code = dev->comp ? SHRD_COMP : 0;
|
|
flag = (dev->comp << 4) | dev->compoff;
|
|
}
|
|
|
|
/* Reset compression stuff */
|
|
dev->comps = dev->comp = dev->compoff = 0;
|
|
|
|
SHRD_SET_HDR (hdr, code, flag, dev->devnum, id, dev->buflen);
|
|
serverSend (dev, ix, hdr, dev->buf, dev->buflen);
|
|
|
|
break;
|
|
|
|
case SHRD_WRITE:
|
|
/* Must be active on the device for this command */
|
|
if (dev->shioactive != id)
|
|
{
|
|
serverError (dev, ix, SHRD_ERROR_NOTACTIVE, cmd,
|
|
"not active on this device");
|
|
break;
|
|
}
|
|
|
|
/* Call the I/O write exit */
|
|
off = fetch_hw (buf);
|
|
rcd = fetch_fw (buf + 2);
|
|
|
|
rc = (dev->hnd->write) (dev, rcd, off, buf + 6, len - 6, &flag);
|
|
SHRDTRACE( "server request write rcd %d off %d len %d flag %2.2x rc=%d",
|
|
rcd, off, len - 6, flag, rc );
|
|
|
|
if (rc < 0)
|
|
code = SHRD_IOERR;
|
|
else
|
|
code = 0;
|
|
|
|
/* Send response back */
|
|
SHRD_SET_HDR (hdr, code, flag, dev->devnum, id, 0);
|
|
serverSend (dev, ix, hdr, NULL, 0);
|
|
|
|
break;
|
|
|
|
case SHRD_SENSE:
|
|
/* Must be active on the device for this command */
|
|
if (dev->shioactive != id)
|
|
{
|
|
serverError (dev, ix, SHRD_ERROR_NOTACTIVE, cmd,
|
|
"not active on this device");
|
|
break;
|
|
}
|
|
|
|
/* Send the sense */
|
|
SHRD_SET_HDR (hdr, 0, CSW_CE | CSW_DE, dev->devnum, id, dev->numsense);
|
|
serverSend (dev, ix, hdr, dev->sense, dev->numsense);
|
|
memset (dev->sense, 0, sizeof(dev->sense));
|
|
dev->sns_pending = 0;
|
|
break;
|
|
|
|
case SHRD_QUERY:
|
|
switch (flag) {
|
|
|
|
case SHRD_USED:
|
|
if (dev->hnd->used)
|
|
rc = (dev->hnd->used) (dev);
|
|
else
|
|
rc = 0;
|
|
store_fw (buf, rc);
|
|
SHRD_SET_HDR (hdr, 0, 0, dev->devnum, id, 4);
|
|
serverSend (dev, ix, hdr, buf, 4);
|
|
break;
|
|
|
|
case SHRD_DEVCHAR:
|
|
SHRD_SET_HDR (hdr, 0, 0, dev->devnum, id, dev->numdevchar);
|
|
serverSend (dev, ix, hdr, dev->devchar, dev->numdevchar);
|
|
break;
|
|
|
|
case SHRD_DEVID:
|
|
SHRD_SET_HDR (hdr, 0, 0, dev->devnum, id, dev->numdevid);
|
|
serverSend (dev, ix, hdr, dev->devid, dev->numdevid);
|
|
break;
|
|
|
|
case SHRD_SERIAL:
|
|
SHRD_SET_HDR (hdr, 0, 0, dev->devnum, id, sizeof( dev->serial ));
|
|
serverSend (dev, ix, hdr, dev->serial, (U32)sizeof( dev->serial ));
|
|
break;
|
|
|
|
case SHRD_CKDCYLS:
|
|
store_fw (buf, dev->ckdcyls);
|
|
SHRD_SET_HDR (hdr, 0, 0, dev->devnum, id, 4);
|
|
serverSend (dev, ix, hdr, buf, 4);
|
|
break;
|
|
|
|
case SHRD_FBAORIGIN:
|
|
store_dw( buf, dev->fbaorigin );
|
|
SHRD_SET_HDR (hdr, 0, 0, dev->devnum, id, 4);
|
|
serverSend (dev, ix, hdr, buf, 4);
|
|
break;
|
|
|
|
case SHRD_FBANUMBLK:
|
|
store_fw (buf, dev->fbanumblk);
|
|
SHRD_SET_HDR (hdr, 0, 0, dev->devnum, id, 4);
|
|
serverSend (dev, ix, hdr, buf, 4);
|
|
break;
|
|
|
|
case SHRD_FBABLKSIZ:
|
|
store_fw (buf, dev->fbablksiz);
|
|
SHRD_SET_HDR (hdr, 0, 0, dev->devnum, id, 4);
|
|
serverSend (dev, ix, hdr, buf, 4);
|
|
break;
|
|
|
|
default:
|
|
serverError (dev, ix, SHRD_ERROR_INVALID, cmd,
|
|
"invalid query request");
|
|
break;
|
|
} /* switch (flag) for SHRD_QUERY */
|
|
break;
|
|
|
|
case SHRD_COMPRESS:
|
|
#if defined( HAVE_ZLIB )
|
|
dev->shrd[ix]->comp = (flag & 0x0f);
|
|
store_hw (buf, dev->shrd[ix]->comp);
|
|
#else
|
|
store_hw (buf, 0);
|
|
#endif
|
|
dev->shrd[ix]->comps = (flag & 0xf0) >> 4;
|
|
SHRD_SET_HDR (hdr, 0, 0, dev->devnum, id, 2);
|
|
serverSend (dev, ix, hdr, buf, 2);
|
|
break;
|
|
|
|
default:
|
|
serverError (dev, ix, SHRD_ERROR_INVALID, cmd,
|
|
"invalid request");
|
|
break;
|
|
} /* switch (cmd) */
|
|
} /* serverRequest */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Locate the SHRD block for a socket (server side). Returns index.
|
|
*-------------------------------------------------------------------*/
|
|
static int serverLocate( DEVBLK* dev, int id, int* avail )
|
|
{
|
|
int i;
|
|
|
|
if (avail)
|
|
*avail = -1;
|
|
|
|
for (i=0; i < SHARED_MAX_SYS; i++)
|
|
{
|
|
// Active slot?
|
|
if (dev->shrd[i])
|
|
{
|
|
// Is this the one they want?
|
|
if (dev->shrd[i]->id == id)
|
|
return i;
|
|
}
|
|
else // (available slot)
|
|
{
|
|
if (avail && *avail < 0)
|
|
*avail = i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Return a new Identifier (server side)
|
|
*-------------------------------------------------------------------*/
|
|
static int serverId (DEVBLK *dev)
|
|
{
|
|
int i; /* Loop index */
|
|
int id; /* Identifier */
|
|
|
|
do {
|
|
dev->shrdid += 1;
|
|
dev->shrdid &= 0xffff;
|
|
if (dev->shrdid == DEV_SYS_LOCAL
|
|
|| dev->shrdid == DEV_SYS_NONE)
|
|
dev->shrdid = 1;
|
|
id = dev->shrdid;
|
|
|
|
for (i = 0; i < SHARED_MAX_SYS; i++)
|
|
if (dev->shrd[i] && dev->shrd[i]->id == id)
|
|
break;
|
|
|
|
} while (i < SHARED_MAX_SYS);
|
|
|
|
return id;
|
|
} /* serverId */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Respond with an error message (server side)
|
|
*-------------------------------------------------------------------*/
|
|
static int serverError (DEVBLK *dev, int ix, int code, int status,
|
|
char *msg)
|
|
{
|
|
int rc; /* Return code */
|
|
size_t len; /* Message length */
|
|
BYTE hdr[SHRD_HDR_SIZE]; /* Header */
|
|
|
|
/* Get message length */
|
|
len = strlen(msg) + 1;
|
|
if (len > SHARED_MAX_MSGLEN)
|
|
len = SHARED_MAX_MSGLEN;
|
|
|
|
SHRD_SET_HDR( hdr, code, status, dev ? dev->devnum : 0,
|
|
ix < 0 ? 0 : dev->shrd[ix]->id, (U16) len );
|
|
|
|
SHRDTRACE( "SERVER ERROR! %2.2x %2.2x: %s", code, status, msg );
|
|
|
|
rc = serverSend( dev, ix, hdr, (BYTE*) msg, (int) len );
|
|
return rc;
|
|
|
|
} /* serverError */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Send data (server side)
|
|
*-------------------------------------------------------------------*/
|
|
static int serverSend (DEVBLK *dev, int ix, BYTE *hdr, BYTE *buf,
|
|
int buflen)
|
|
{
|
|
int rc; /* Return code */
|
|
int sock; /* Socket number */
|
|
BYTE code; /* Header code */
|
|
BYTE status; /* Header status */
|
|
U16 devnum; /* Header device number */
|
|
int id; /* Header identifier */
|
|
int len; /* Header length */
|
|
int hdrlen; /* Header length + other data*/
|
|
BYTE *sendbuf = NULL; /* Send buffer */
|
|
int sendlen; /* Send length */
|
|
BYTE cbuf[SHRD_HDR_SIZE + 65536]; /* Combined buffer */
|
|
|
|
/* Make buf, buflen consistent if no additional data to be sent */
|
|
if (buf == NULL) buflen = 0;
|
|
else if (buflen == 0) buf = NULL;
|
|
|
|
/* Calculate length of header, may contain additional data */
|
|
SHRD_GET_HDR(hdr, code, status, devnum, id, len);
|
|
hdrlen = SHRD_HDR_SIZE + (len - buflen);
|
|
sendlen = hdrlen + buflen;
|
|
|
|
/* Check if buf is adjacent to the header */
|
|
if (buf && hdr + hdrlen == buf)
|
|
{
|
|
hdrlen += buflen;
|
|
buf = NULL;
|
|
buflen = 0;
|
|
}
|
|
|
|
/* Send only the header buffer if 'buf' is empty */
|
|
if (buflen == 0) sendbuf = hdr;
|
|
|
|
/* Get socket number; if 'ix' < 0 we don't have a device yet */
|
|
if (ix >= 0)
|
|
sock = dev->shrd[ix]->fd;
|
|
else
|
|
{
|
|
sock = -ix;
|
|
dev = NULL;
|
|
}
|
|
|
|
SHRDHDRTRACE( "server send", hdr );
|
|
|
|
#if defined( HAVE_ZLIB )
|
|
/* Compress the buf */
|
|
if (ix >= 0 && dev->shrd[ix]->comp != 0
|
|
&& code == SHRD_OK && status == 0
|
|
&& hdrlen - SHRD_HDR_SIZE <= SHRD_COMP_MAX_OFF
|
|
&& buflen >= SHARED_COMPRESS_MINLEN)
|
|
{
|
|
unsigned long newlen;
|
|
int off = hdrlen - SHRD_HDR_SIZE;
|
|
sendbuf = cbuf;
|
|
newlen = sizeof(cbuf) - hdrlen;
|
|
memcpy (cbuf, hdr, hdrlen);
|
|
rc = compress2 (cbuf + hdrlen, &newlen,
|
|
buf, buflen, dev->shrd[ix]->comp);
|
|
if (rc == Z_OK && (int)newlen < buflen)
|
|
{
|
|
/* Setup to use the compressed buffer */
|
|
sendlen = hdrlen + newlen;
|
|
buflen = 0;
|
|
code = SHRD_COMP;
|
|
status = (SHRD_LIBZ << 4) | off;
|
|
SHRD_SET_HDR (cbuf, code, status, devnum, id, (U16)(newlen + off));
|
|
SHRDHDRTRACE2( "server send", cbuf, "(compressed)" );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Build combined (hdr + data) buffer */
|
|
if (buflen > 0)
|
|
{
|
|
sendbuf = cbuf;
|
|
memcpy (cbuf, hdr, hdrlen);
|
|
memcpy (cbuf + hdrlen, buf, buflen);
|
|
}
|
|
|
|
/* Send the combined header and data */
|
|
rc = send (sock, sendbuf, sendlen, 0);
|
|
|
|
/* Process return code */
|
|
if (rc < 0)
|
|
{
|
|
// "%1d:%04X Shared: error in send id %d: %s"
|
|
WRMSG( HHC00729, "E", LCSS_DEVNUM, id, strerror( HSO_errno ));
|
|
dev->shrd[ix]->disconnect = 1;
|
|
}
|
|
|
|
return rc;
|
|
|
|
} /* serverSend */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Determine if a client can be disconnected (server side)
|
|
*-------------------------------------------------------------------*/
|
|
static bool serverDisconnectable( DEVBLK* dev, int ix )
|
|
{
|
|
return
|
|
(1
|
|
&& !dev->shrd[ix]->waiting
|
|
&& !dev->shrd[ix]->pending
|
|
&& dev->shioactive != dev->shrd[ix]->id
|
|
);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Disconnect a client (server side)
|
|
* dev->lock *must* be held
|
|
*-------------------------------------------------------------------*/
|
|
static void serverDisconnect (DEVBLK *dev, int ix)
|
|
{
|
|
int id; /* Client identifier */
|
|
int i; /* Loop index */
|
|
|
|
id = dev->shrd[ix]->id;
|
|
|
|
//FIXME: Handle a disconnected busy client better
|
|
// Perhaps a disconnect timeout value... this will
|
|
// give the client time to reconnect.
|
|
|
|
/* If the device is active by the client then extricate it.
|
|
This is *not* a good situation */
|
|
if (dev->shioactive == id)
|
|
{
|
|
// "%1d:%04X Shared: busy client being removed id %d %s"
|
|
WRMSG( HHC00730, "W", LCSS_DEVNUM, id, dev->reserved ? "reserved" : "" );
|
|
|
|
/* Call the I/O release exit if reserved by this client */
|
|
if (dev->reserved && dev->hnd->release)
|
|
(dev->hnd->release) (dev);
|
|
|
|
/* Call the channel program end exit */
|
|
if (dev->hnd->end)
|
|
(dev->hnd->end) (dev);
|
|
|
|
/* Reset any 'waiting' bits */
|
|
for (i = 0; i < SHARED_MAX_SYS; i++)
|
|
if (dev->shrd[i])
|
|
dev->shrd[i]->waiting = 0;
|
|
|
|
/* Make the device available */
|
|
if (dev->suspended) {
|
|
dev->shioactive = DEV_SYS_LOCAL;
|
|
dev->busy = 1;
|
|
}
|
|
else
|
|
{
|
|
dev->shioactive = DEV_SYS_NONE;
|
|
dev->busy = 0;
|
|
}
|
|
|
|
/* Notify any waiters */
|
|
if (dev->shiowaiters)
|
|
signal_condition (&dev->shiocond);
|
|
}
|
|
|
|
if (MLVL( VERBOSE ))
|
|
// "%1d:%04X Shared: %s disconnected id %d"
|
|
WRMSG( HHC00731, "I", LCSS_DEVNUM, dev->shrd[ix]->ipaddr, id );
|
|
|
|
/* Release the SHRD block */
|
|
close_socket (dev->shrd[ix]->fd);
|
|
free (dev->shrd[ix]->ipaddr);
|
|
free (dev->shrd[ix]);
|
|
dev->shrd[ix] = NULL;
|
|
|
|
dev->shrdconn--;
|
|
} /* serverDisconnect */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Return client ip
|
|
*-------------------------------------------------------------------*/
|
|
static char *clientip (int sock)
|
|
{
|
|
int rc; /* Return code */
|
|
struct sockaddr_in client; /* Client address structure */
|
|
socklen_t namelen; /* Length of client structure*/
|
|
|
|
namelen = sizeof(client);
|
|
rc = getpeername (sock, (struct sockaddr *)&client, &namelen);
|
|
return inet_ntoa(client.sin_addr);
|
|
|
|
} /* clientip */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Find device by device number
|
|
*-------------------------------------------------------------------*/
|
|
static DEVBLK *findDevice (U16 devnum)
|
|
{
|
|
DEVBLK *dev; /* -> Device block */
|
|
|
|
for (dev = sysblk.firstdev; dev != NULL; dev = dev->nextdev)
|
|
if (dev->devnum == devnum) break;
|
|
return dev;
|
|
|
|
} /* findDevice */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Connect a new client This is essentially the device thread
|
|
*-------------------------------------------------------------------*/
|
|
static void *serverConnect (void *psock)
|
|
{
|
|
int csock; /* Connection socket */
|
|
int rc; /* Return code */
|
|
BYTE cmd; /* Request command */
|
|
BYTE flag; /* Request flag */
|
|
U16 devnum; /* Request device number */
|
|
int id; /* Request id */
|
|
int len; /* Request data length */
|
|
int ix; /* Client index */
|
|
DEVBLK *dev=NULL; /* -> Device block */
|
|
time_t now; /* Current time */
|
|
fd_set selset; /* Read bit map for select */
|
|
int maxfd; /* Max fd for select */
|
|
struct timeval wait = {0}; /* Wait time for select */
|
|
BYTE hdr[SHRD_HDR_SIZE + 65536]; /* Header + buffer */
|
|
BYTE *buf = hdr + SHRD_HDR_SIZE; /* Buffer */
|
|
char *ipaddr = NULL; /* IP addr of connected peer */
|
|
char threadname[16] = {0};
|
|
static const bool server_req = true;
|
|
|
|
// We are (or will be) the "dev->shrdtid" thread...
|
|
|
|
csock = *(int*)psock;
|
|
free( psock );
|
|
ipaddr = clientip( csock );
|
|
|
|
SHRDTRACE( "server connect %s sock %d", ipaddr, csock );
|
|
|
|
rc = recvData( csock, hdr, buf, 65536, server_req );
|
|
if (rc < 0)
|
|
{
|
|
// "Shared: connect to IP %s failed"
|
|
WRMSG( HHC00732, "E", ipaddr );
|
|
close_socket( csock );
|
|
return NULL;
|
|
}
|
|
|
|
SHRD_GET_HDR( hdr, cmd, flag, devnum, id, len );
|
|
|
|
/* Error if not a connect request */
|
|
if (id == 0 && cmd != SHRD_CONNECT)
|
|
{
|
|
serverError( NULL, -csock, SHRD_ERROR_NOTCONN, cmd,
|
|
"not a connect request" );
|
|
close_socket( csock );
|
|
return NULL;
|
|
}
|
|
|
|
/* Locate the device */
|
|
if (!(dev = findDevice( devnum )))
|
|
{
|
|
serverError( NULL, -csock, SHRD_ERROR_NODEVICE, cmd,
|
|
"device not found" );
|
|
close_socket( csock );
|
|
return NULL;
|
|
}
|
|
|
|
/* Obtain the device lock */
|
|
OBTAIN_DEVLOCK( dev );
|
|
|
|
/* Find an available slot for the connection */
|
|
if ((rc = serverLocate( dev, id, &ix )) >= 0)
|
|
{
|
|
RELEASE_DEVLOCK( dev );
|
|
serverError( NULL, -csock, SHRD_ERROR_NODEVICE, cmd,
|
|
"already connected" );
|
|
close_socket( csock );
|
|
return NULL;
|
|
}
|
|
|
|
/* Error if no available slot */
|
|
if (ix < 0)
|
|
{
|
|
RELEASE_DEVLOCK( dev );
|
|
serverError( NULL, -csock, SHRD_ERROR_NOTAVAIL, cmd,
|
|
"too many connections" );
|
|
close_socket( csock );
|
|
return NULL;
|
|
}
|
|
|
|
/* Obtain SHRD block */
|
|
if (!(dev->shrd[ix] = calloc( sizeof( SHRD ), 1 )))
|
|
{
|
|
RELEASE_DEVLOCK( dev );
|
|
serverError( NULL, -csock, SHRD_ERROR_NOMEM, cmd,
|
|
"calloc() failure" );
|
|
close_socket( csock );
|
|
return NULL;
|
|
}
|
|
|
|
/* Initialize the SHRD block */
|
|
dev->shrd[ix]->pending = 1;
|
|
dev->shrd[ix]->havehdr = 1;
|
|
if (id == 0) id = serverId( dev );
|
|
dev->shrd[ix]->id = id;
|
|
dev->shrd[ix]->fd = csock;
|
|
dev->shrd[ix]->ipaddr = strdup(ipaddr);
|
|
dev->shrd[ix]->time = time (NULL);
|
|
dev->shrd[ix]->purgen = -1;
|
|
dev->shrdconn++;
|
|
SHRD_SET_HDR( dev->shrd[ix]->hdr, cmd, flag, devnum, id, len );
|
|
|
|
if (MLVL( VERBOSE ))
|
|
// "%1d:%04X Shared: %s connected id %d"
|
|
WRMSG( HHC00733, "I", LCSS_DEVNUM, ipaddr, id );
|
|
|
|
/* Return if device thread already active */
|
|
if (dev->shrdtid)
|
|
{
|
|
RELEASE_DEVLOCK( dev );
|
|
return NULL;
|
|
}
|
|
|
|
/* This thread will be the shared device thread (dev->shrdtid) */
|
|
|
|
dev->shrdtid = thread_id();
|
|
|
|
MSGBUF( threadname, "shrd dev %1d:%04X", LCSS_DEVNUM );
|
|
if (MLVL( VERBOSE ))
|
|
LOG_THREAD_BEGIN( threadname );
|
|
|
|
/* Keep looping while there are still clients connected to our device */
|
|
/* PROGRAMMING NOTE: the below loop runs with the device lock held! */
|
|
while (dev->shrdconn)
|
|
{
|
|
FD_ZERO( &selset );
|
|
maxfd = -1;
|
|
|
|
/* Get the current time */
|
|
now = time( NULL );
|
|
|
|
/* For each remote system connected to our device... */
|
|
for (ix = 0; ix < SHARED_MAX_SYS; ix++)
|
|
{
|
|
/* Is there a client at this slot? */
|
|
if (dev->shrd[ix])
|
|
{
|
|
/* Stop as soon as we find a pending request */
|
|
if (dev->shrd[ix]->pending && !dev->shrd[ix]->waiting)
|
|
break; // (go process pending request)
|
|
|
|
/* Disconnect if not a valid socket */
|
|
if (!socket_is_socket( dev->shrd[ix]->fd ))
|
|
dev->shrd[ix]->disconnect = 1;
|
|
|
|
/* See if the connection can be timed out */
|
|
else if (1
|
|
&& (now - dev->shrd[ix]->time) > SHARED_TIMEOUT
|
|
&& serverDisconnectable( dev, ix )
|
|
)
|
|
dev->shrd[ix]->disconnect = 1;
|
|
|
|
/* Disconnect if the disconnect bit is set */
|
|
if (dev->shrd[ix]->disconnect)
|
|
serverDisconnect( dev, ix );
|
|
|
|
/* Otherwise set the fd if not waiting */
|
|
else if (!dev->shrd[ix]->waiting)
|
|
{
|
|
FD_SET( dev->shrd[ix]->fd, &selset );
|
|
|
|
if (dev->shrd[ix]->fd >= maxfd)
|
|
maxfd = dev->shrd[ix]->fd + 1;
|
|
|
|
SHRDTRACE( "select set %d id=%d",
|
|
dev->shrd[ix]->fd, dev->shrd[ix]->id );
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Wait for request if all pending requests were processed */
|
|
if (ix >= SHARED_MAX_SYS)
|
|
{
|
|
/* If our select set is empty, there are not any clients
|
|
connected to our device (and dev->shrdconn SHOULD now
|
|
be zero) and thus we have nothing to do. If we don't
|
|
have any clients connected to our device then we are
|
|
serving no purpose so we should just exit our thread.
|
|
*/
|
|
if (maxfd < 0) // (if nothing to select)
|
|
continue; // (then exit our thread)
|
|
|
|
/* Wait for a client to send us a request */
|
|
wait.tv_sec = 0;
|
|
wait.tv_usec = SHARED_SELECT_WAIT_MSECS * 1000;
|
|
|
|
RELEASE_DEVLOCK( dev );
|
|
{
|
|
dev->shrdwait = 1;
|
|
{
|
|
rc = select( maxfd, &selset, NULL, NULL, &wait );
|
|
}
|
|
dev->shrdwait = 0;
|
|
|
|
SHRDTRACE("serverConnect: select rc %d", rc );
|
|
}
|
|
OBTAIN_DEVLOCK( dev );
|
|
|
|
/* Timeout; no one has any requests for us at this time */
|
|
if (rc == 0)
|
|
continue;
|
|
|
|
/* Did the select fail? */
|
|
if (rc < 0 )
|
|
{
|
|
/* Try again if temporary error */
|
|
if (HSO_errno == HSO_EINTR || HSO_errno == HSO_EBADF)
|
|
continue;
|
|
|
|
// "Shared: error in function %s: %s"
|
|
WRMSG( HHC00735, "E", "select()", strerror( HSO_errno ));
|
|
break; // (exit our thread)
|
|
}
|
|
|
|
/* One or more sockets in our select set is ready, meaning
|
|
one or more remote clients connected to our device has
|
|
a request it needs us to process. Loop through our list
|
|
of connected clients and set the 'pending' request bit
|
|
for each one whose socket that select said is FD_ISSET.
|
|
*/
|
|
for (ix = 0; ix < SHARED_MAX_SYS; ix++)
|
|
{
|
|
if (1
|
|
/* Is there a client at this slot? */
|
|
&& dev->shrd[ix]
|
|
/* Did select() say its socket is set? */
|
|
&& FD_ISSET( dev->shrd[ix]->fd, &selset )
|
|
)
|
|
{
|
|
/* Indicate this client has a pending request */
|
|
dev->shrd[ix]->pending = 1;
|
|
SHRDTRACE("select isset %d id=%d",
|
|
dev->shrd[ix]->fd, dev->shrd[ix]->id );
|
|
}
|
|
}
|
|
continue; // (re-iterate to process all pending requests)
|
|
}
|
|
|
|
/* Found a pending request */
|
|
RELEASE_DEVLOCK( dev );
|
|
{
|
|
SHRDTRACE("select ready %d id=%d",
|
|
dev->shrd[ix]->fd, dev->shrd[ix]->id );
|
|
|
|
if (dev->shrd[ix]->havehdr)
|
|
{
|
|
/* Copy the saved start/resume packet */
|
|
memcpy( hdr, dev->shrd[ix]->hdr, SHRD_HDR_SIZE );
|
|
dev->shrd[ix]->havehdr = 0;
|
|
dev->shrd[ix]->waiting = 0;
|
|
}
|
|
else
|
|
{
|
|
/* Read the request packet */
|
|
if ((rc = recvData( dev->shrd[ix]->fd, hdr, buf, 65536, 1 )) < 0)
|
|
{
|
|
// "%1d:%04X Shared: error in receive from %s id %d"
|
|
WRMSG( HHC00734, "E", LCSS_DEVNUM, dev->shrd[ix]->ipaddr, dev->shrd[ix]->id );
|
|
dev->shrd[ix]->disconnect = 1;
|
|
dev->shrd[ix]->pending = 0;
|
|
OBTAIN_DEVLOCK( dev );
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* Process the request */
|
|
serverRequest( dev, ix, hdr, buf );
|
|
}
|
|
OBTAIN_DEVLOCK( dev );
|
|
|
|
/* If the 'waiting' bit is on then the start/resume request
|
|
failed because the device is busy on some other remote
|
|
system. We only need to save the header because the data
|
|
is ignored for start/resume.
|
|
*/
|
|
if (dev->shrd[ix]->waiting)
|
|
{
|
|
memcpy( dev->shrd[ix]->hdr, hdr, SHRD_HDR_SIZE );
|
|
dev->shrd[ix]->havehdr = 1;
|
|
}
|
|
else
|
|
dev->shrd[ix]->pending = 0;
|
|
|
|
} /* while (dev->shrdconn) */
|
|
|
|
dev->shrdtid = 0;
|
|
RELEASE_DEVLOCK( dev );
|
|
|
|
if (MLVL( VERBOSE ))
|
|
LOG_THREAD_END( threadname );
|
|
|
|
return NULL;
|
|
|
|
} /* serverConnect */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Trace routine for tracing SHRD_HDR
|
|
*-------------------------------------------------------------------*/
|
|
static void shrdhdrtrc( DEVBLK* dev, const char* msg, const BYTE* hdr,
|
|
const char* msg2 )
|
|
{
|
|
BYTE cmd, code; U16 devnum; int id, len; char buf[4];
|
|
SHRD_GET_HDR( hdr, cmd, code, devnum, id, len );
|
|
MSGBUF( buf, "%2.2x", cmd );
|
|
SHRDTRACE( "%-18s : %s(%2.2x) %2.2x dev %4.4x id %d len %d%s%s",
|
|
msg, shrdcmd2str( cmd ), cmd, code, devnum, id, len,
|
|
msg2 ? " " : "", msg2 ? msg2 : "" );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------
|
|
* General trace routine for shared devices
|
|
*-------------------------------------------------------------------*/
|
|
static void shrdtrc( DEVBLK* dev, const char* fmt, ... )
|
|
{
|
|
bool tracing;
|
|
struct timeval tv;
|
|
SHRD_TRACE tracemsg;
|
|
va_list vl;
|
|
char buf[32];
|
|
|
|
/* If the device is being traced or stepped, we also print a
|
|
trace message (WITHOUT a timestamp) directly to the panel.
|
|
Otherwise we build a trace message WITH a timestamp prefix
|
|
and enter it into out trace table if one exists. If neither
|
|
is true (not tracing or stepping AND no trace table) then
|
|
there's nothing for us to do so we return immediately.
|
|
*/
|
|
tracing = (dev && dev->ccwtrace);
|
|
|
|
OBTAIN_SHRDTRACE_LOCK();
|
|
|
|
if (!tracing && !sysblk.shrdtrace)
|
|
{
|
|
RELEASE_SHRDTRACE_LOCK();
|
|
return; // (nothing for us to do!)
|
|
}
|
|
|
|
ASSERT( tracing || sysblk.shrdtrace );
|
|
|
|
/* Build the timestamp portion of the trace message */
|
|
gettimeofday( &tv, NULL );
|
|
FormatTIMEVAL( &tv, buf, sizeof( buf ));
|
|
STRLCPY( tracemsg, buf + 11 ); // (skip "YYYY-MM-DD ")
|
|
|
|
/* Append the devnum to the trace message */
|
|
MSGBUF( buf, " %4.4X ", dev ? dev->devnum : 0 );
|
|
STRLCAT( tracemsg, buf );
|
|
|
|
/* Now format the rest of the trace message following that part */
|
|
va_start( vl, fmt );
|
|
vsnprintf( (char*) tracemsg + strlen( tracemsg ),
|
|
sizeof( tracemsg ) - strlen( tracemsg ), fmt, vl );
|
|
|
|
/* Log the trace message directly to the panel (WITHOUT the
|
|
timestamp prefix) if the device is being traced/stepped. */
|
|
if (tracing)
|
|
// "Shared: %s"
|
|
WRMSG( HHC00743, "I", tracemsg + 16 ); // (skip "HH:MM:SS.uuuuuu ")
|
|
|
|
/* Copy the trace message into the trace table (if it exists) */
|
|
shrdtrclog_locked( tracemsg );
|
|
|
|
RELEASE_SHRDTRACE_LOCK();
|
|
}
|
|
|
|
/*-------------------------------------------------------------------
|
|
* General non-device-specific trace routine
|
|
*-------------------------------------------------------------------*/
|
|
static void shrdgentrc( const char* fmt, ... )
|
|
{
|
|
struct timeval tv;
|
|
SHRD_TRACE tracemsg;
|
|
va_list vl;
|
|
char buf[32];
|
|
|
|
OBTAIN_SHRDTRACE_LOCK();
|
|
|
|
if (!sysblk.shrdtrace)
|
|
{
|
|
RELEASE_SHRDTRACE_LOCK();
|
|
return; // (nothing for us to do!)
|
|
}
|
|
|
|
ASSERT( sysblk.shrdtrace );
|
|
|
|
/* Build the timestamp portion of the trace message */
|
|
gettimeofday( &tv, NULL );
|
|
FormatTIMEVAL( &tv, buf, sizeof( buf ));
|
|
STRLCPY( tracemsg, buf + 11 ); // (skip "YYYY-MM-DD ")
|
|
|
|
/* Now format the rest of the trace message following that part */
|
|
va_start( vl, fmt );
|
|
vsnprintf( (char*) tracemsg + strlen( tracemsg ),
|
|
sizeof( tracemsg ) - strlen( tracemsg ), fmt, vl );
|
|
|
|
/* Copy the trace message into the trace table (if it exists) */
|
|
shrdtrclog_locked( tracemsg );
|
|
|
|
RELEASE_SHRDTRACE_LOCK();
|
|
}
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Add the trace message to the trace table (lock held)
|
|
*-------------------------------------------------------------------*/
|
|
static void shrdtrclog_locked( const char* tracemsg )
|
|
{
|
|
/* Copy the trace message into the trace table (if it exists) */
|
|
if (sysblk.shrdtrace)
|
|
{
|
|
/* Grab pointer to next available table entry and then
|
|
bump the pointer to the next entry for next time.
|
|
*/
|
|
SHRD_TRACE* currmsg = sysblk.shrdtracep++;
|
|
|
|
if (sysblk.shrdtracep >= sysblk.shrdtracex)
|
|
sysblk.shrdtracep = sysblk.shrdtrace;
|
|
|
|
/* Copy message (WITH timestamp) into trace table */
|
|
strlcpy( (char*) currmsg, (const char*) tracemsg, sizeof( SHRD_TRACE ));
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------
|
|
* shutdown_shared_server
|
|
*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void shutdown_shared_server( void* unused )
|
|
{
|
|
OBTAIN_SHRDLOCK();
|
|
{
|
|
shutdown_shared_server_locked( unused );
|
|
}
|
|
RELEASE_SHRDLOCK();
|
|
}
|
|
|
|
/*-------------------------------------------------------------------
|
|
* shutdown_shared_server_locked
|
|
*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void shutdown_shared_server_locked( void* unused )
|
|
{
|
|
UNREFERENCED( unused );
|
|
|
|
if (sysblk.shrdport || sysblk.shrdtid)
|
|
{
|
|
sysblk.shrdport = 0;
|
|
|
|
if (sysblk.shrdtid)
|
|
wait_condition( &sysblk.shrdcond, &sysblk.shrdlock );
|
|
}
|
|
ASSERT( !sysblk.shrdport && !sysblk.shrdtid );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Shared device server thread: accept connect from remote client
|
|
*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void* shared_server( void* arg )
|
|
{
|
|
bool shutdown=false; /* shutdown flag */
|
|
int rc = -32767; /* Return code */
|
|
int hi; /* Hi fd for select */
|
|
int lsock; /* inet socket for listening */
|
|
int usock; /* unix socket for listening */
|
|
int rsock; /* Ready socket */
|
|
int csock; /* Socket for conversation */
|
|
int *psock; /* Pointer to socket */
|
|
struct sockaddr_in server; /* Server address structure */
|
|
#if defined( HAVE_SYS_UN_H )
|
|
struct sockaddr_un userver; /* Unix address structure */
|
|
#endif
|
|
int optval; /* Argument for setsockopt */
|
|
fd_set selset; /* Read bit map for select */
|
|
TID tid; /* Negotiation thread id */
|
|
char threadname[16] = {0};
|
|
struct timeval timeout = {0};
|
|
|
|
// We are the "sysblk.shrdtid" thread...
|
|
|
|
UNREFERENCED( arg );
|
|
|
|
/* Display thread started message on control panel */
|
|
MSGBUF( threadname, "shrd srvr %d.%d", SHARED_VERSION, SHARED_RELEASE );
|
|
LOG_THREAD_BEGIN( threadname );
|
|
|
|
/* Obtain a internet socket */
|
|
if ((lsock = socket( AF_INET, SOCK_STREAM, 0 )) < 0)
|
|
{
|
|
// "Shared: error in function %s: %s"
|
|
WRMSG( HHC00735, "E", "socket()", strerror( HSO_errno ));
|
|
return NULL;
|
|
}
|
|
|
|
/* Obtain a unix socket */
|
|
#if defined( HAVE_SYS_UN_H )
|
|
if ((usock = socket( AF_UNIX, SOCK_STREAM, 0 )) < 0)
|
|
{
|
|
// "Shared: error in function %s: %s"
|
|
WRMSG( HHC00735, "W", "socket()", strerror( HSO_errno ));
|
|
}
|
|
#else
|
|
usock = -1;
|
|
#endif
|
|
|
|
/* Allow previous instance of socket to be reused */
|
|
optval = 1;
|
|
setsockopt( lsock, SOL_SOCKET, SO_REUSEADDR,
|
|
(GETSET_SOCKOPT_T*) &optval, sizeof( optval ));
|
|
|
|
/* Prepare the sockaddr structure for the bind */
|
|
memset( &server, 0, sizeof(server) );
|
|
server.sin_family = AF_INET;
|
|
server.sin_addr.s_addr = INADDR_ANY;
|
|
server.sin_port = htons( sysblk.shrdport );
|
|
|
|
/* Attempt to bind the internet socket to the port */
|
|
while (sysblk.shrdport)
|
|
{
|
|
rc = bind( lsock, (struct sockaddr*) &server, sizeof( server ));
|
|
if (rc == 0 || HSO_errno != HSO_EADDRINUSE)
|
|
break;
|
|
// "Shared: waiting for port %u to become free"
|
|
WRMSG( HHC00736, "W", sysblk.shrdport );
|
|
SLEEP( 10 );
|
|
} /* end while */
|
|
|
|
if (rc != 0 || !sysblk.shrdport)
|
|
{
|
|
// "Shared: error in function %s: %s"
|
|
WRMSG( HHC00735, "E", "bind()", strerror( HSO_errno ));
|
|
close_socket( lsock );
|
|
close_socket( usock );
|
|
return NULL;
|
|
}
|
|
|
|
#if defined( HAVE_SYS_UN_H )
|
|
/* Bind the unix socket */
|
|
if (usock >= 0)
|
|
{
|
|
userver.sun_family = AF_UNIX;
|
|
sprintf( userver.sun_path, "/tmp/hercules_shared.%d", sysblk.shrdport );
|
|
unlink( userver.sun_path );
|
|
fchmod( usock, 0700 );
|
|
|
|
rc = bind( usock, (struct sockaddr*) &userver, sizeof( userver ));
|
|
|
|
if (rc < 0)
|
|
{
|
|
// "Shared: error in function %s: %s"
|
|
WRMSG( HHC00735, "W", "bind()", strerror( HSO_errno ));
|
|
close( usock );
|
|
usock = -1;
|
|
}
|
|
}
|
|
#endif // defined( HAVE_SYS_UN_H )
|
|
|
|
/* Put the sockets into listening state */
|
|
rc = listen( lsock, SHARED_MAX_SYS );
|
|
|
|
if (rc < 0)
|
|
{
|
|
// "Shared: error in function %s: %s"
|
|
WRMSG( HHC00735, "E", "listen()", strerror( HSO_errno ));
|
|
close_socket( lsock );
|
|
close_socket( usock );
|
|
return NULL;
|
|
}
|
|
|
|
if (usock >= 0)
|
|
{
|
|
rc = listen( usock, SHARED_MAX_SYS );
|
|
|
|
if (rc < 0)
|
|
{
|
|
// "Shared: error in function %s: %s"
|
|
WRMSG( HHC00735, "W", "listen()", strerror( HSO_errno ));
|
|
close_socket( usock );
|
|
usock = -1;
|
|
}
|
|
}
|
|
|
|
csock = -1;
|
|
|
|
if (lsock < usock)
|
|
hi = usock + 1;
|
|
else
|
|
hi = lsock + 1;
|
|
|
|
// "Shared: waiting for shared device requests on port %u"
|
|
WRMSG( HHC00737, "I", sysblk.shrdport );
|
|
|
|
/* Define shared server thread shutdown routine */
|
|
hdl_addshut( "shutdown_shared_server", shutdown_shared_server, NULL );
|
|
|
|
/* Handle connection requests and attention interrupts */
|
|
while (1)
|
|
{
|
|
/* Continue running (looping) as long as shrdport is defined */
|
|
OBTAIN_SHRDLOCK();
|
|
{
|
|
shutdown = (sysblk.shrdport ? false : true);
|
|
}
|
|
RELEASE_SHRDLOCK();
|
|
|
|
if (shutdown)
|
|
break;
|
|
|
|
/* Initialize the select parameters */
|
|
FD_ZERO( &selset );
|
|
FD_SET( lsock, &selset );
|
|
|
|
if (usock >= 0)
|
|
FD_SET( usock, &selset );
|
|
|
|
/* Wait for a file descriptor to become ready */
|
|
timeout.tv_sec = 0;
|
|
timeout.tv_usec = SHARED_SELECT_WAIT_MSECS * 1000;
|
|
rc = select( hi, &selset, NULL, NULL, &timeout );
|
|
|
|
SHRDGENTRACE("shared_server: select rc %d", rc );
|
|
|
|
if (rc == 0)
|
|
continue;
|
|
|
|
if (rc < 0 )
|
|
{
|
|
if (HSO_errno == HSO_EINTR)
|
|
continue;
|
|
|
|
// "Shared: error in function %s: %s"
|
|
WRMSG( HHC00735, "E", "select()", strerror( HSO_errno ));
|
|
break;
|
|
}
|
|
|
|
/* If a client connection request has arrived then accept it */
|
|
if (FD_ISSET( lsock, &selset ))
|
|
rsock = lsock;
|
|
else if (usock >= 0 && FD_ISSET( usock, &selset ))
|
|
rsock = usock;
|
|
else
|
|
rsock = -1;
|
|
|
|
/* Accept the connection and create conversation socket */
|
|
if (rsock > 0)
|
|
{
|
|
if ((csock = accept( rsock, NULL, NULL )) < 0)
|
|
{
|
|
// "Shared: error in function %s: %s"
|
|
WRMSG( HHC00735, "E", "accept()", strerror( HSO_errno ));
|
|
continue;
|
|
}
|
|
|
|
if (!(psock = malloc( sizeof( csock ))))
|
|
{
|
|
char buf[40];
|
|
MSGBUF( buf, "malloc(%d)", (int) sizeof( csock ));
|
|
// "Shared: error in function %s: %s"
|
|
WRMSG( HHC00735, "E", buf, strerror( HSO_errno ));
|
|
close_socket( csock );
|
|
continue;
|
|
}
|
|
*psock = csock;
|
|
|
|
/* Create a thread to complete the client connection */
|
|
rc = create_thread( &tid, DETACHED,
|
|
serverConnect, psock, "serverConnect" );
|
|
if (rc)
|
|
{
|
|
// "Error in function create_thread(): %s"
|
|
WRMSG( HHC00102, "E", strerror( rc ));
|
|
close_socket( csock );
|
|
}
|
|
|
|
} /* end if(rsock) */
|
|
|
|
} /* end while (1) */
|
|
|
|
/* Remove shut entry so we can do a new 'hdl_addshut' next time */
|
|
if (!sysblk.shutdown)
|
|
hdl_delshut( shutdown_shared_server, NULL );
|
|
|
|
/* Close the listening sockets */
|
|
close_socket( lsock );
|
|
|
|
#if defined( HAVE_SYS_UN_H )
|
|
if (usock >= 0)
|
|
{
|
|
close_socket( usock );
|
|
unlink( userver.sun_path );
|
|
}
|
|
#endif
|
|
|
|
/* Notify "shutdown_shared_server" that we've exited */
|
|
OBTAIN_SHRDLOCK();
|
|
{
|
|
sysblk.shrdtid = 0;
|
|
signal_condition( &sysblk.shrdcond );
|
|
}
|
|
RELEASE_SHRDLOCK();
|
|
|
|
LOG_THREAD_END( threadname );
|
|
|
|
return NULL;
|
|
|
|
} /* end function shared_server */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Define number of Shared Device Server trace table entries
|
|
*-------------------------------------------------------------------*/
|
|
DLL_EXPORT int shrd_cmd( int argc, char* argv[], char* cmdline )
|
|
{
|
|
char buf[256];
|
|
char *kw, *op, c;
|
|
char *strtok_str = NULL;
|
|
|
|
UNREFERENCED( cmdline );
|
|
|
|
UPPER_ARGV_0( argv );
|
|
|
|
/* Get keyword and operand */
|
|
if (0
|
|
|| argc < 1
|
|
|| argc > 2
|
|
|| (argc > 1 && strlen( argv[1] ) >= _countof( buf ))
|
|
)
|
|
{
|
|
// "Shared: invalid or missing argument"
|
|
WRMSG( HHC00738, "E" );
|
|
return -1;
|
|
}
|
|
|
|
if (argc < 2)
|
|
{
|
|
OBTAIN_SHRDTRACE_LOCK();
|
|
{
|
|
MSGBUF( buf, "TRACE=%d DTAX=%d", sysblk.shrdtracen, sysblk.shrddtax );
|
|
}
|
|
RELEASE_SHRDTRACE_LOCK();
|
|
// "%-14s: %s"
|
|
WRMSG( HHC02203, "I", argv[0], buf );
|
|
return 0;
|
|
}
|
|
|
|
// Format: "SHRD [TRACE[=nnnn]]" where nnnn is #of table entries.
|
|
// Enter the command with no argument to display the current value.
|
|
// Use "shrd trace" by itself to print the current table.
|
|
|
|
STRLCPY( buf, argv[1] );
|
|
|
|
kw = strtok_r( buf, "=", &strtok_str );
|
|
op = strtok_r( NULL, " \t", &strtok_str );
|
|
|
|
if (!kw)
|
|
{
|
|
// "Shared: invalid or missing keyword"
|
|
WRMSG( HHC00739, "E" );
|
|
return -1;
|
|
}
|
|
|
|
if (strcasecmp( kw, "TRACE" ) == 0)
|
|
{
|
|
SHRD_TRACE* shrdtrace;
|
|
SHRD_TRACE* shrdtracep;
|
|
SHRD_TRACE* shrdtracex;
|
|
int shrdtracen;
|
|
|
|
OBTAIN_SHRDTRACE_LOCK();
|
|
|
|
shrdtrace = sysblk.shrdtrace; // (ptr to beginning of table)
|
|
shrdtracep = sysblk.shrdtracep; // (ptr to current/next entry)
|
|
shrdtracex = sysblk.shrdtracex; // (ptr past the end of table)
|
|
|
|
/* Print trace table if no TRACE operand was specified */
|
|
if (!op)
|
|
{
|
|
shared_print_trace_table_locked();
|
|
RELEASE_SHRDTRACE_LOCK();
|
|
return 0;
|
|
}
|
|
|
|
/* Operand specified: get size of requested table */
|
|
if (sscanf( op, "%d%c", &shrdtracen, &c ) != 1)
|
|
{
|
|
// "Shared: invalid or missing value %s"
|
|
WRMSG( HHC00740, "E", op );
|
|
RELEASE_SHRDTRACE_LOCK();
|
|
return -1;
|
|
}
|
|
|
|
/* Free existing table */
|
|
free( sysblk.shrdtrace );
|
|
sysblk.shrdtrace = NULL;
|
|
sysblk.shrdtracex = NULL;
|
|
sysblk.shrdtracep = NULL;
|
|
sysblk.shrdtracen = 0;
|
|
|
|
/* Allocate new table */
|
|
if (shrdtracen > 0)
|
|
{
|
|
if (!(shrdtrace = calloc( (size_t) shrdtracen, sizeof( SHRD_TRACE ))))
|
|
{
|
|
char buf[40];
|
|
MSGBUF( buf, "calloc(%d, %d)", (int) shrdtracen, (int) sizeof( SHRD_TRACE ));
|
|
// "Shared: error in function %s: %s"
|
|
WRMSG( HHC00735, "E", buf, strerror( errno ));
|
|
RELEASE_SHRDTRACE_LOCK();
|
|
return -1;
|
|
}
|
|
|
|
sysblk.shrdtracen = shrdtracen;
|
|
sysblk.shrdtracex = shrdtrace + shrdtracen;
|
|
sysblk.shrdtrace = shrdtrace;
|
|
sysblk.shrdtracep = shrdtrace;
|
|
}
|
|
|
|
if (MLVL( VERBOSE ))
|
|
{
|
|
// "%-14s set to %s"
|
|
MSGBUF( buf, "TRACE=%d", sysblk.shrdtracen );
|
|
WRMSG( HHC02204, "I", argv[0], buf );
|
|
}
|
|
|
|
RELEASE_SHRDTRACE_LOCK();
|
|
return 0;
|
|
}
|
|
|
|
if (strcasecmp( kw, "DTAX" ) == 0) // Dump Table At Exit
|
|
{
|
|
int dtax;
|
|
|
|
if (!op)
|
|
{
|
|
// "Shared: invalid or missing value %s"
|
|
WRMSG( HHC00740, "E", kw );
|
|
return -1;
|
|
}
|
|
if (0
|
|
|| sscanf( op, "%d%c", &dtax, &c ) != 1
|
|
|| (dtax != 0 && dtax != 1)
|
|
)
|
|
{
|
|
// "Shared: invalid or missing value %s"
|
|
WRMSG( HHC00740, "E", op );
|
|
return -1;
|
|
}
|
|
|
|
OBTAIN_SHRDTRACE_LOCK();
|
|
{
|
|
sysblk.shrddtax = dtax ? true : false;
|
|
}
|
|
RELEASE_SHRDTRACE_LOCK();
|
|
|
|
// "%-14s set to %s"
|
|
MSGBUF( buf, "DTAX=%d", sysblk.shrddtax );
|
|
WRMSG( HHC02204, "I", argv[0], buf );
|
|
|
|
return 0;
|
|
}
|
|
|
|
// "Shared: invalid or missing keyword %s"
|
|
WRMSG( HHC00741, "E", kw );
|
|
return -1;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Print Shared Device Server trace table entries
|
|
*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void shared_print_trace_table()
|
|
{
|
|
OBTAIN_SHRDTRACE_LOCK();
|
|
{
|
|
shared_print_trace_table_locked();
|
|
}
|
|
RELEASE_SHRDTRACE_LOCK();
|
|
}
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Print Shared Device Server trace table entries
|
|
*-------------------------------------------------------------------*/
|
|
static void shared_print_trace_table_locked()
|
|
{
|
|
/* Does a trace table exist? */
|
|
if (sysblk.shrdtrace)
|
|
{
|
|
SHRD_TRACE* current = sysblk.shrdtracep;
|
|
bool printed = false;
|
|
|
|
do
|
|
{
|
|
if (current[0][0])
|
|
{
|
|
// "Shared: %s"
|
|
WRMSG( HHC00743, "I", (char*) current );
|
|
printed = true;
|
|
}
|
|
|
|
if (++current >= sysblk.shrdtracex)
|
|
current = sysblk.shrdtrace;
|
|
}
|
|
while (current != sysblk.shrdtracep);
|
|
|
|
if (!printed)
|
|
{
|
|
// "Shared: %s"
|
|
WRMSG( HHC00743, "I", "(none)" );
|
|
}
|
|
|
|
/* Now that it's been printed, reset it to empty */
|
|
memset( sysblk.shrdtrace, 0, sysblk.shrdtracen * sizeof( SHRD_TRACE ));
|
|
}
|
|
else
|
|
// "Shared: %s"
|
|
WRMSG( HHC00743, "I", "(NULL)" );
|
|
}
|
|
|
|
DEVHND shared_ckd_device_hndinfo = {
|
|
&shared_ckd_init, /* Device Initialization */
|
|
&ckd_dasd_execute_ccw, /* Device CCW execute */
|
|
&shared_ckd_close, /* Device Close */
|
|
&ckd_dasd_query_device, /* Device Query */
|
|
NULL, /* Device Extended Query */
|
|
&shared_start, /* Device Start channel pgm */
|
|
&shared_end, /* Device End channel pgm */
|
|
&shared_start, /* Device Resume channel pgm */
|
|
&shared_end, /* Device Suspend channel pgm */
|
|
NULL, /* Device Halt channel pgm */
|
|
&shared_ckd_read, /* Device Read */
|
|
&shared_ckd_write, /* Device Write */
|
|
&shared_used, /* Device Query used */
|
|
&shared_reserve, /* Device Reserve */
|
|
&shared_release, /* Device Release */
|
|
NULL, /* Device Attention */
|
|
NULL, /* Immediate CCW Codes */
|
|
NULL, /* Signal Adapter Input */
|
|
NULL, /* Signal Adapter Output */
|
|
NULL, /* Signal Adapter Sync */
|
|
NULL, /* Signal Adapter Output Mult */
|
|
NULL, /* QDIO subsys desc */
|
|
NULL, /* QDIO set subchan ind */
|
|
&ckd_dasd_hsuspend, /* Hercules suspend */
|
|
&ckd_dasd_hresume /* Hercules resume */
|
|
};
|
|
|
|
DEVHND shared_fba_device_hndinfo = {
|
|
&shared_fba_init, /* Device Initialization */
|
|
&fba_dasd_execute_ccw, /* Device CCW execute */
|
|
&shared_fba_close, /* Device Close */
|
|
&fba_dasd_query_device, /* Device Query */
|
|
NULL, /* Device Extended Query */
|
|
&shared_start, /* Device Start channel pgm */
|
|
&shared_end, /* Device End channel pgm */
|
|
&shared_start, /* Device Resume channel pgm */
|
|
&shared_end, /* Device Suspend channel pgm */
|
|
NULL, /* Device Halt channel pgm */
|
|
&shared_ckd_read, /* Device Read */
|
|
&shared_ckd_write, /* Device Write */
|
|
&shared_used, /* Device Query used */
|
|
&shared_reserve, /* Device Reserve */
|
|
&shared_release, /* Device Release */
|
|
NULL, /* Device Attention */
|
|
NULL, /* Immediate CCW Codes */
|
|
NULL, /* Signal Adapter Input */
|
|
NULL, /* Signal Adapter Output */
|
|
NULL, /* Signal Adapter Sync */
|
|
NULL, /* Signal Adapter Output Mult */
|
|
NULL, /* QDIO subsys desc */
|
|
NULL, /* QDIO set subchan ind */
|
|
&fba_dasd_hsuspend, /* Hercules suspend */
|
|
&fba_dasd_hresume /* Hercules resume */
|
|
};
|
|
|
|
#else // !defined( OPTION_SHARED_DEVICES )
|
|
|
|
int shared_update_notify( DEVBLK* dev, int block )
|
|
{
|
|
UNREFERENCED( dev );
|
|
UNREFERENCED( block );
|
|
return 0;
|
|
}
|
|
int shared_ckd_init( DEVBLK* dev, int argc, char* argv[] )
|
|
{
|
|
UNREFERENCED( dev );
|
|
UNREFERENCED( argc );
|
|
UNREFERENCED( argv );
|
|
return -1;
|
|
}
|
|
int shared_fba_init( DEVBLK* dev, int argc, char* argv[] )
|
|
{
|
|
UNREFERENCED( dev );
|
|
UNREFERENCED( argc );
|
|
UNREFERENCED( argv );
|
|
return -1;
|
|
}
|
|
void* shared_server( void* arg )
|
|
{
|
|
UNREFERENCED( arg );
|
|
// "Shared: OPTION_SHARED_DEVICES not defined"
|
|
WRMSG( HHC00742, "E" );
|
|
return NULL;
|
|
}
|
|
int shrd_cmd( int argc, char* argv[], char* cmdline )
|
|
{
|
|
UNREFERENCED( argc );
|
|
UNREFERENCED( argv );
|
|
UNREFERENCED( cmdline );
|
|
// "Shared: OPTION_SHARED_DEVICES not defined"
|
|
WRMSG( HHC00742, "E" );
|
|
return -1;
|
|
}
|
|
#endif /* defined( OPTION_SHARED_DEVICES ) */
|