hercules-3.13/ 0000775 0001754 0001754 00000000000 13162135614 010267 5 0000000 0000000 hercules-3.13/hsocket.h 0000664 0001754 0001754 00000013307 13066015061 012020 0000000 0000000 /* HSOCKET.H (c) Copyright Roger Bowler, 2005-2009 */
/* Equates for socket functions */
/* This header file contains equates for the socket functions */
/* and constants whose values differ between Unix and Winsock */
#if !defined(_HSOCKET_H)
#define _HSOCKET_H
#ifndef _HSOCKET_C_
#ifndef _HUTIL_DLL_
#define HSOCK_DLL_IMPORT DLL_IMPORT
#else
#define HSOCK_DLL_IMPORT extern
#endif
#else
#define HSOCK_DLL_IMPORT DLL_EXPORT
#endif
/*-------------------------------------------------------------------*/
/* Socket related constants related to 'shutdown' API call */
/*-------------------------------------------------------------------*/
#ifdef _MSVC_
/* Map SUS\*nix constants to Windows socket equivalents */
#define SHUT_RD SD_RECEIVE
#define SHUT_WR SD_SEND
#define SHUT_RDWR SD_BOTH
#endif
#if defined(_WINSOCKAPI_)
/*-------------------------------------------------------------------*/
/* Equates for systems which use the Winsock API */
/*-------------------------------------------------------------------*/
#define get_HSO_errno() ((int)WSAGetLastError())
#define set_HSO_errno(e) (WSASetLastError(e))
#define HSO_errno get_HSO_errno()
#define HSO_EINTR WSAEINTR
#define HSO_EBADF WSAEBADF
#define HSO_EACCES WSAEACCES
#define HSO_EFAULT WSAEFAULT
#define HSO_EINVAL WSAEINVAL
#define HSO_EMFILE WSAEMFILE
#define HSO_EWOULDBLOCK WSAEWOULDBLOCK
#define HSO_EINPROGRESS WSAEINPROGRESS
#define HSO_EALREADY WSAEALREADY
#define HSO_ENOTSOCK WSAENOTSOCK
#define HSO_EDESTADDRREQ WSAEDESTADDRREQ
#define HSO_EMSGSIZE WSAEMSGSIZE
#define HSO_EPROTOTYPE WSAEPROTOTYPE
#define HSO_ENOPROTOOPT WSAENOPROTOOPT
#define HSO_EPROTONOSUPPORT WSAEPROTONOSUPPORT
#define HSO_ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT
#define HSO_EOPNOTSUPP WSAEOPNOTSUPP
#define HSO_EPFNOSUPPORT WSAEPFNOSUPPORT
#define HSO_EAFNOSUPPORT WSAEAFNOSUPPORT
#define HSO_EADDRINUSE WSAEADDRINUSE
#define HSO_EADDRNOTAVAIL WSAEADDRNOTAVAIL
#define HSO_ENETDOWN WSAENETDOWN
#define HSO_ENETUNREACH WSAENETUNREACH
#define HSO_ENETRESET WSAENETRESET
#define HSO_ECONNABORTED WSAECONNABORTED
#define HSO_ECONNRESET WSAECONNRESET
#define HSO_ENOBUFS WSAENOBUFS
#define HSO_EISCONN WSAEISCONN
#define HSO_ENOTCONN WSAENOTCONN
#define HSO_ESHUTDOWN WSAESHUTDOWN
#define HSO_ETOOMANYREFS WSAETOOMANYREFS
#define HSO_ETIMEDOUT WSAETIMEDOUT
#define HSO_ECONNREFUSED WSAECONNREFUSED
#define HSO_ELOOP WSAELOOP
#define HSO_ENAMETOOLONG WSAENAMETOOLONG
#define HSO_EHOSTDOWN WSAEHOSTDOWN
#define HSO_EHOSTUNREACH WSAEHOSTUNREACH
#define HSO_ENOTEMPTY WSAENOTEMPTY
#define HSO_EPROCLIM WSAEPROCLIM
#define HSO_EUSERS WSAEUSERS
#define HSO_EDQUOT WSAEDQUOT
#define HSO_ESTALE WSAESTALE
#define HSO_EREMOTE WSAEREMOTE
#else
/*-------------------------------------------------------------------*/
/* Equates for systems which use the Berkeley sockets API */
/*-------------------------------------------------------------------*/
#define get_HSO_errno() (errno)
#define set_HSO_errno(e) (errno=(e))
#define HSO_errno get_HSO_errno()
#define HSO_EINTR EINTR
#define HSO_EBADF EBADF
#define HSO_EACCES EACCES
#define HSO_EFAULT EFAULT
#define HSO_EINVAL EINVAL
#define HSO_EMFILE EMFILE
#define HSO_EWOULDBLOCK EWOULDBLOCK
#define HSO_EINPROGRESS EINPROGRESS
#define HSO_EALREADY EALREADY
#define HSO_ENOTSOCK ENOTSOCK
#define HSO_EDESTADDRREQ EDESTADDRREQ
#define HSO_EMSGSIZE EMSGSIZE
#define HSO_EPROTOTYPE EPROTOTYPE
#define HSO_ENOPROTOOPT ENOPROTOOPT
#define HSO_EPROTONOSUPPORT EPROTONOSUPPORT
#define HSO_ESOCKTNOSUPPORT ESOCKTNOSUPPORT
#define HSO_EOPNOTSUPP EOPNOTSUPP
#define HSO_EPFNOSUPPORT EPFNOSUPPORT
#define HSO_EAFNOSUPPORT EAFNOSUPPORT
#define HSO_EADDRINUSE EADDRINUSE
#define HSO_EADDRNOTAVAIL EADDRNOTAVAIL
#define HSO_ENETDOWN ENETDOWN
#define HSO_ENETUNREACH ENETUNREACH
#define HSO_ENETRESET ENETRESET
#define HSO_ECONNABORTED ECONNABORTED
#define HSO_ECONNRESET ECONNRESET
#define HSO_ENOBUFS ENOBUFS
#define HSO_EISCONN EISCONN
#define HSO_ENOTCONN ENOTCONN
#define HSO_ESHUTDOWN ESHUTDOWN
#define HSO_ETOOMANYREFS ETOOMANYREFS
#define HSO_ETIMEDOUT ETIMEDOUT
#define HSO_ECONNREFUSED ECONNREFUSED
#define HSO_ELOOP ELOOP
#define HSO_ENAMETOOLONG ENAMETOOLONG
#define HSO_EHOSTDOWN EHOSTDOWN
#define HSO_EHOSTUNREACH EHOSTUNREACH
#define HSO_ENOTEMPTY ENOTEMPTY
#define HSO_EPROCLIM EPROCLIM
#define HSO_EUSERS EUSERS
#define HSO_EDQUOT EDQUOT
#define HSO_ESTALE ESTALE
#define HSO_EREMOTE EREMOTE
#endif
/*-------------------------------------------------------------------*/
/* Local function definitions */
/*-------------------------------------------------------------------*/
HSOCK_DLL_IMPORT int read_socket(int fd, void *ptr, int nbytes);
HSOCK_DLL_IMPORT int write_socket(int fd, const void *ptr, int nbytes);
#endif /*!defined(_HSOCKET_H)*/
hercules-3.13/tapedev.h 0000664 0001754 0001754 00000123245 13066015061 012013 0000000 0000000 /* TAPEDEV.H (c) Copyright Ivan Warren and others, 2003-2009 */
/* Tape Device Handler Structure Definitions */
/*-------------------------------------------------------------------*/
/* This header file contains tape related structures and defines */
/* for the Hercules ESA/390 emulator. */
/*-------------------------------------------------------------------*/
#ifndef __TAPEDEV_H__
#define __TAPEDEV_H__
#include "scsitape.h" /* SCSI Tape handling functions */
#include "htypes.h" /* Hercules struct typedefs */
#include "opcode.h" /* device_attention, SETMODE, etc. */
#include "parser.h" /* generic parameter string parser */
/*-------------------------------------------------------------------*/
/* Internal macro definitions */
/*-------------------------------------------------------------------*/
#define MAX_BLKLEN 65535 /* Maximum I/O buffer size */
#define TAPE_UNLOADED "*" /* Name for unloaded drive */
/*-------------------------------------------------------------------*/
/* Definitions for 3420/3480 sense bytes */
/*-------------------------------------------------------------------*/
#define SENSE1_TAPE_NOISE 0x80 /* Noise */
#define SENSE1_TAPE_TUA 0x40 /* TU Status A (ready) */
#define SENSE1_TAPE_TUB 0x20 /* TU Status B (not ready) */
#define SENSE1_TAPE_7TRK 0x10 /* 7-track feature */
#define SENSE1_TAPE_RSE 0x10 /* Record sequence error */
#define SENSE1_TAPE_LOADPT 0x08 /* Tape is at load point */
#define SENSE1_TAPE_WRT 0x04 /* Tape is in write status */
#define SENSE1_TAPE_FP 0x02 /* File protect status */
#define SENSE1_TAPE_NCA 0x01 /* Not capable */
#define SENSE4_TAPE_EOT 0x20 /* Tape indicate (EOT) */
#define SENSE5_TAPE_SRDCHK 0x08 /* Start read check */
#define SENSE5_TAPE_PARTREC 0x04 /* Partial record */
#define SENSE7_TAPE_LOADFAIL 0x01 /* Load failure */
/*-------------------------------------------------------------------*/
/* ISW : Internal error types used to build Device Dependent Sense */
/*-------------------------------------------------------------------*/
#define TAPE_BSENSE_TAPEUNLOADED 0 /* I/O Attempted but no tape loaded */
#define TAPE_BSENSE_TAPELOADFAIL 1 /* I/O and load failed */
#define TAPE_BSENSE_READFAIL 2 /* Error reading block */
#define TAPE_BSENSE_WRITEFAIL 3 /* Error writing block */
#define TAPE_BSENSE_BADCOMMAND 4 /* The CCW code is not known
or sequence error */
#define TAPE_BSENSE_INCOMPAT 5 /* The CCW code is known
but is not unsupported */
#define TAPE_BSENSE_WRITEPROTECT 6 /* Write CCW code was issued
to a read-only media */
#define TAPE_BSENSE_EMPTYTAPE 7 /* A read was issued but the
tape is empty */
#define TAPE_BSENSE_ENDOFTAPE 8 /* A read was issued past the
end of the tape or a write
was issued and there is no
space left on the tape */
#define TAPE_BSENSE_LOADPTERR 9 /* BSF/BSR/RdBW attempted
from BOT */
#define TAPE_BSENSE_FENCED 10 /* Media damaged - unload
or /reload required */
#define TAPE_BSENSE_BADALGORITHM 11 /* Bad compression - HET
tape compressed with an
unsuported method */
#define TAPE_BSENSE_RUN_SUCCESS 12 /* Rewind Unload success */
#define TAPE_BSENSE_STATUSONLY 13 /* No exception occured */
#define TAPE_BSENSE_LOCATEERR 14 /* Can't find block or TM */
#define TAPE_BSENSE_READTM 15 /* A Tape Mark was read */
#define TAPE_BSENSE_BLOCKSHORT 17 /* Short Tape block */
#define TAPE_BSENSE_ITFERROR 18 /* Interface error (SCSI
driver unexpected err) */
#define TAPE_BSENSE_REWINDFAILED 19 /* Rewind operation failed */
#define TAPE_BSENSE_UNSOLICITED 20 /* Sense without UC */
/*-------------------------------------------------------------------*/
/* Definitions for 3480 and later commands */
/*-------------------------------------------------------------------*/
/* Format control byte for Load Display command */
#define FCB_FS 0xE0 /* Function Select bits... */
#define FCB_FS_READYGO 0x00 /* Display msg until motion, */
/* or until msg is updated */
#define FCB_FS_UNMOUNT 0x20 /* Display msg until unloaded*/
#define FCB_FS_MOUNT 0x40 /* Display msg until loaded */
#define FCB_FS_RESET_DISPLAY 0x80 /* Reset display (clear Host */
/* msg; replace w/Unit msg) */
#define FCB_FS_NOP 0x60 /* No-op */
#define FCB_FS_UMOUNTMOUNT 0xE0 /* Display msg 1 until tape */
/* is unloaded, then msg 2 */
/* until tape is loaded */
#define FCB_AM 0x10 /* Alternate between msg 1/2 */
#define FCB_BM 0x08 /* Blink message */
#define FCB_M2 0x04 /* Display only message 2 */
#define FCB_RESV 0x02 /* (reserved) */
#define FCB_AL 0x01 /* Activate AutoLoader on */
/* mount/unmount messages */
/* Mode Set commands */
#define MSET_WRITE_IMMED 0x20 /* Tape-Write-Immediate mode */
#define MSET_SUPVR_INHIBIT 0x10 /* Supervisor Inhibit mode */
#define MSET_IDRC 0x08 /* IDRC mode */
/* Path state byte for Sense Path Group ID command */
#define SPG_PATHSTAT 0xC0 /* Pathing status bits... */
#define SPG_PATHSTAT_RESET 0x00 /* ...reset */
#define SPG_PATHSTAT_RESV 0x40 /* ...reserved bit setting */
#define SPG_PATHSTAT_UNGROUPED 0x80 /* ...ungrouped */
#define SPG_PATHSTAT_GROUPED 0xC0 /* ...grouped */
#define SPG_PARTSTAT 0x30 /* Partitioning status bits..*/
#define SPG_PARTSTAT_IENABLED 0x00 /* ...implicitly enabled */
#define SPG_PARTSTAT_RESV 0x10 /* ...reserved bit setting */
#define SPG_PARTSTAT_DISABLED 0x20 /* ...disabled */
#define SPG_PARTSTAT_XENABLED 0x30 /* ...explicitly enabled */
#define SPG_PATHMODE 0x08 /* Path mode bit... */
#define SPG_PATHMODE_SINGLE 0x00 /* ...single path mode */
#define SPG_PATHMODE_RESV 0x08 /* ...reserved bit setting */
#define SPG_RESERVED 0x07 /* Reserved bits, must be 0 */
/* Function control byte for Set Path Group ID command */
#define SPG_SET_MULTIPATH 0x80 /* Set multipath mode */
#define SPG_SET_COMMAND 0x60 /* Set path command bits... */
#define SPG_SET_ESTABLISH 0x00 /* ...establish group */
#define SPG_SET_DISBAND 0x20 /* ...disband group */
#define SPG_SET_RESIGN 0x40 /* ...resign from group */
#define SPG_SET_COMMAND_RESV 0x60 /* ...reserved bit setting */
#define SPG_SET_RESV 0x1F /* Reserved bits, must be 0 */
/* Perform Subsystem Function order byte for PSF command */
#define PSF_ORDER_PRSD 0x18 /* Prep for Read Subsys Data */
#define PSF_ACTION_SSD_ATNMSG 0x03 /* ..Attention Message */
#define PSF_ORDER_SSIC 0x1B /* Set Special Intercept Cond*/
#define PSF_ORDER_MNS 0x1C /* Message Not Supported */
#define PSF_ORDER_AFEL 0x80 /* Activate Forced Error Log.*/
#define PSF_ORDER_DFEL 0x81 /* Deact. Forced Error Log. */
#define PSF_ACTION_FEL_IMPLICIT 0x01 /* ..Implicit (De)Activate */
#define PSF_ACTION_FEL_EXPLICIT 0x02 /* ..Explicit (De)Activate */
#define PSF_ORDER_AAC 0x82 /* Activate Access Control */
#define PSF_ORDER_DAC 0x83 /* Deact. Access Control */
#define PSF_ACTION_AC_LWP 0x80 /* ..Logical Write Protect */
#define PSF_ACTION_AC_DCD 0x10 /* ..Data Compaction Default */
#define PSF_ACTION_AC_DCR 0x02 /* ..Data Check Recovery */
#define PSF_ACTION_AC_ER 0x01 /* ..Extended Recovery */
#define PSF_ORDER_RVF 0x90 /* Reset Volume Fenced */
#define PSF_ORDER_PIN_DEV 0xA1 /* Pin Device */
#define PSF_ACTION_PIN_CU0 0x00 /* ..Control unit 0 */
#define PSF_ACTION_PIN_CU1 0x01 /* ..Control unit 1 */
#define PSF_ORDER_UNPIN_DEV 0xA2 /* Unpin Device */
#define PSF_FLAG_ZERO 0x00 /* Must be zero for all ord. */
/* Control Access Function Control */
#define CAC_FUNCTION 0xC0 /* Function control bits */
#define CAC_SET_PASSWORD 0x00 /* ..Set Password */
#define CAC_COND_ENABLE 0x80 /* ..Conditional Enable */
#define CAC_COND_DISABLE 0x40 /* ..Conditional Disable */
/*-------------------------------------------------------------------*/
/* Definitions for tape device type field in device block */
/*-------------------------------------------------------------------*/
#define TAPEDEVT_UNKNOWN 0 /* AWSTAPE format disk file */
#define TAPEDEVT_AWSTAPE 1 /* AWSTAPE format disk file */
#define TAPEDEVT_OMATAPE 2 /* OMATAPE format disk files */
#define TAPEDEVT_SCSITAPE 3 /* Physical SCSI tape */
#define TAPEDEVT_HETTAPE 4 /* HET format disk file */
#define TAPEDEVT_FAKETAPE 5 /* Flex FakeTape disk format */
/*-------------------------------------------------------------------*/
/* Fish - macros for checking SCSI tape device-independent status */
/*-------------------------------------------------------------------*/
#if defined(OPTION_SCSI_TAPE)
#define STS_TAPEMARK(dev) GMT_SM ( (dev)->sstat )
#define STS_EOF(dev) GMT_EOF ( (dev)->sstat )
#define STS_BOT(dev) GMT_BOT ( (dev)->sstat )
#define STS_EOT(dev) GMT_EOT ( (dev)->sstat )
#define STS_EOD(dev) GMT_EOD ( (dev)->sstat )
#define STS_WR_PROT(dev) GMT_WR_PROT ( (dev)->sstat )
#define STS_ONLINE(dev) GMT_ONLINE ( (dev)->sstat )
#define STS_MOUNTED(dev) ((dev)->fd >= 0 && !GMT_DR_OPEN( (dev)->sstat ))
#define STS_NOT_MOUNTED(dev) (!STS_MOUNTED(dev))
#endif
#define AUTOLOAD_WAIT_FOR_TAPEMOUNT_INTERVAL_SECS (5) /* (default) */
/*-------------------------------------------------------------------*/
/* Structure definition for HET/AWS/OMA tape block headers */
/*-------------------------------------------------------------------*/
/*
* The integer fields in the HET, AWSTAPE and OMATAPE headers are
* encoded in the Intel format (i.e. the bytes of the integer are held
* in reverse order). For this reason the integers are defined as byte
* arrays, and the bytes are fetched individually in order to make
* the code portable across architectures which use either the Intel
* format or the S/370 format.
*
* Block length fields contain the length of the emulated tape block
* and do not include the length of the header.
*
* For the AWSTAPE and HET formats:
* - the first block has a previous block length of zero
* - a tapemark is indicated by a header with a block length of zero
* and a flag byte of X'40'
*
* For the OMATAPE format:
* - the first block has a previous header offset of X'FFFFFFFF'
* - a tapemark is indicated by a header with a block length of
* X'FFFFFFFF'
* - each block is followed by padding bytes if necessary to ensure
* that the next header starts on a 16-byte boundary
*
*/
typedef struct _AWSTAPE_BLKHDR
{ /*
* PROGRAMMING NOTE: note that for AWS tape files, the "current
* chunk size" comes FIRST and the "previous chunk size" comes
* second. This is the complete opposite from the way it is for
* Flex FakeTape. Also note that for AWS the size fields are in
* LITTLE endian binary whereas for Flex FakeTape they're a BIG
* endian ASCII hex-string.
*/
HWORD curblkl; /* Length of this block */
HWORD prvblkl; /* Length of previous block */
BYTE flags1; /* Flags byte 1 (see below) */
BYTE flags2; /* Flags byte 2 */
/* Definitions for AWSTAPE_BLKHDR flags byte 1 */
#define AWSTAPE_FLAG1_NEWREC 0x80 /* Start of new record */
#define AWSTAPE_FLAG1_TAPEMARK 0x40 /* Tape mark */
#define AWSTAPE_FLAG1_ENDREC 0x20 /* End of record */
}
AWSTAPE_BLKHDR;
/*-------------------------------------------------------------------*/
/* Structure definition for OMA block header */
/*-------------------------------------------------------------------*/
typedef struct _OMATAPE_BLKHDR
{
FWORD curblkl; /* Length of this block */
FWORD prvhdro; /* Offset of previous block
header from start of file */
FWORD omaid; /* OMA identifier (contains
ASCII characters "@HDF") */
FWORD resv; /* Reserved */
}
OMATAPE_BLKHDR;
/*-------------------------------------------------------------------*/
/* Structure definition for OMA tape descriptor array */
/*-------------------------------------------------------------------*/
typedef struct _OMATAPE_DESC
{
int fd; /* File Descriptor for file */
char filename[256]; /* Filename of data file */
char format; /* H=HEADERS,T=TEXT,F=FIXED,X=Tape Mark */
BYTE resv; /* Reserved for alignment */
U16 blklen; /* Fixed block length */
}
OMATAPE_DESC;
/*-------------------------------------------------------------------*/
/* Structure definition for Flex FakeTape block headers */
/*-------------------------------------------------------------------*/
/*
* The character length fields in a Flex FakeTape header are in BIG
* endian ASCII hex. That is to say, when the length field is ASCII
* "0123" (i.e. 0x30, 0x31, 0x32, 0x33), the length of the block is
* decimal 291 bytes (0x0123 == 291).
*
* The two block length fields are followed by an XOR "check" field
* calculated as the XOR of the two preceding length fields and is
* used to verify the integrity of the header.
*
* The Flex FakeTape tape format does not support any flag fields
* in its header and thus does not support any type of compression.
*/
typedef struct _FAKETAPE_BLKHDR
{ /*
* PROGRAMMING NOTE: note that for Flex FakeTapes, the "previous
* chunk size" comes FIRST, followed by the "current chunk size"
* second. This is the complete opposite from the way it is for
* AWS tape files. Also note that for Flex FakeTape the size fields
* are in BIG endian ASCII hex-string whereas for AWS tapes
* they're LITTLE endian binary.
*/
char sprvblkl[4]; /* length of previous block */
char scurblkl[4]; /* length of this block */
char sxorblkl[4]; /* XOR both lengths together */
}
FAKETAPE_BLKHDR;
/*-------------------------------------------------------------------*/
/* Tape Auto-Loader table entry */
/*-------------------------------------------------------------------*/
struct TAPEAUTOLOADENTRY
{
char *filename;
int argc;
char **argv;
};
/*-------------------------------------------------------------------*/
/* Tape AUTOMOUNT CCWS directory control */
/*-------------------------------------------------------------------*/
struct TAMDIR
{
TAMDIR *next; /* ptr to next entry or NULL */
char *dir; /* resolved directory value */
int len; /* strlen(dir) */
int rej; /* 1 == reject, 0 == accept */
};
/*-------------------------------------------------------------------*/
/* Generic media-handler-call parameters block */
/*-------------------------------------------------------------------*/
typedef struct _GENTMH_PARMS
{
int action; // action code (i.e. "what to do")
DEVBLK* dev; // -> device block
BYTE* unitstat; // -> unit status
BYTE code; // CCW opcode
// TODO: define whatever additional arguments may be needed...
}
GENTMH_PARMS;
/*-------------------------------------------------------------------*/
/* Generic media-handler-call action codes */
/*-------------------------------------------------------------------*/
#define GENTMH_SCSI_ACTION_UPDATE_STATUS (0)
//efine GENTMH_AWS_ACTION_xxxxx... (x)
//efine GENTMH_HET_ACTION_xxxxx... (x)
//efine GENTMH_OMA_ACTION_xxxxx... (x)
/*-------------------------------------------------------------------*/
/* Tape media I/O function vector table layout */
/*-------------------------------------------------------------------*/
struct TAPEMEDIA_HANDLER
{
int (*generic) (GENTMH_PARMS*); // (generic call)
int (*open) (DEVBLK*, BYTE *unitstat, BYTE code);
void (*close) (DEVBLK*);
int (*read) (DEVBLK*, BYTE *buf, BYTE *unitstat, BYTE code);
int (*write) (DEVBLK*, BYTE *buf, U16 blklen, BYTE *unitstat, BYTE code);
int (*rewind) (DEVBLK*, BYTE *unitstat, BYTE code);
int (*bsb) (DEVBLK*, BYTE *unitstat, BYTE code);
int (*fsb) (DEVBLK*, BYTE *unitstat, BYTE code);
int (*bsf) (DEVBLK*, BYTE *unitstat, BYTE code);
int (*fsf) (DEVBLK*, BYTE *unitstat, BYTE code);
int (*wtm) (DEVBLK*, BYTE *unitstat, BYTE code);
int (*sync) (DEVBLK*, BYTE *unitstat, BYTE code);
int (*dse) (DEVBLK*, BYTE *unitstat, BYTE code);
int (*erg) (DEVBLK*, BYTE *unitstat, BYTE code);
int (*tapeloaded) (DEVBLK*, BYTE *unitstat, BYTE code);
int (*passedeot) (DEVBLK*);
/* readblkid o/p values are returned in BIG-ENDIAN guest format */
int (*readblkid) (DEVBLK*, BYTE* logical, BYTE* physical);
/* locateblk i/p value is passed in little-endian host format */
int (*locateblk) (DEVBLK*, U32 blockid, BYTE *unitstat, BYTE code);
};
/*-------------------------------------------------------------------*/
/* Functions defined in TAPEDEV.C */
/*-------------------------------------------------------------------*/
extern int tapedev_init_handler (DEVBLK *dev, int argc, char *argv[]);
extern int tapedev_close_device (DEVBLK *dev );
extern void tapedev_query_device (DEVBLK *dev, char **class, int buflen, char *buffer);
extern void autoload_init (DEVBLK *dev, int ac, char **av);
extern int autoload_mount_first (DEVBLK *dev);
extern int autoload_mount_next (DEVBLK *dev);
extern void autoload_close (DEVBLK *dev);
extern void autoload_global_parms (DEVBLK *dev, char *par);
extern void autoload_clean_entry (DEVBLK *dev, int ix);
extern void autoload_tape_entry (DEVBLK *dev, char *fn, char **strtokw);
extern int autoload_mount_tape (DEVBLK *dev, int alix);
extern void* autoload_wait_for_tapemount_thread (void *db);
extern int gettapetype (DEVBLK *dev, char **short_descr);
extern int gettapetype_byname (DEVBLK *dev);
extern int gettapetype_bydata (DEVBLK *dev);
extern int mountnewtape (DEVBLK *dev, int argc, char **argv);
extern void GetDisplayMsg (DEVBLK *dev, char *msgbfr, size_t lenbfr);
extern int IsAtLoadPoint (DEVBLK *dev);
extern void ReqAutoMount (DEVBLK *dev);
extern void UpdateDisplay (DEVBLK *dev);
extern int return_false1 (DEVBLK *dev);
extern int write_READONLY5 (DEVBLK *dev, BYTE *bfr, U16 blklen, BYTE *unitstat, BYTE code);
extern int is_tapeloaded_filename (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern int write_READONLY (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern int no_operation (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern int readblkid_virtual (DEVBLK*, BYTE* logical, BYTE* physical);
extern int locateblk_virtual (DEVBLK*, U32 blockid, BYTE *unitstat, BYTE code);
extern int generic_tmhcall (GENTMH_PARMS*);
/*-------------------------------------------------------------------*/
/* Functions (and data areas) defined in TAPECCWS.C */
/*-------------------------------------------------------------------*/
typedef void TapeSenseFunc( int, DEVBLK*, BYTE*, BYTE ); // (sense handling function)
#define TAPEDEVTYPELIST_ENTRYSIZE (5) // #of int's per 'TapeDevtypeList' table entry
extern int TapeDevtypeList[];
extern BYTE* TapeCommandTable[];
extern TapeSenseFunc* TapeSenseTable[];
//tern BYTE TapeCommandsXXXX[256]...
extern BYTE TapeImmedCommands[];
extern int TapeCommandIsValid (BYTE code, U16 devtype, BYTE *rustat);
extern void tapedev_execute_ccw (DEVBLK *dev, BYTE code, BYTE flags,
BYTE chained, U16 count, BYTE prevcode, int ccwseq,
BYTE *iobuf, BYTE *more, BYTE *unitstat, U16 *residual);
extern void load_display (DEVBLK *dev, BYTE *buf, U16 count);
extern void build_senseX (int ERCode, DEVBLK *dev, BYTE *unitstat, BYTE ccwcode);
extern void build_sense_3410 (int ERCode, DEVBLK *dev, BYTE *unitstat, BYTE ccwcode);
extern void build_sense_3420 (int ERCode, DEVBLK *dev, BYTE *unitstat, BYTE ccwcode);
extern void build_sense_3410_3420 (int ERCode, DEVBLK *dev, BYTE *unitstat, BYTE ccwcode);
extern void build_sense_3480_etal (int ERCode, DEVBLK *dev, BYTE *unitstat, BYTE ccwcode);
extern void build_sense_3490 (int ERCode, DEVBLK *dev, BYTE *unitstat, BYTE ccwcode);
extern void build_sense_3590 (int ERCode, DEVBLK *dev, BYTE *unitstat, BYTE ccwcode);
extern void build_sense_Streaming (int ERCode, DEVBLK *dev, BYTE *unitstat, BYTE ccwcode);
/*-------------------------------------------------------------------*/
/* Calculate I/O Residual */
/*-------------------------------------------------------------------*/
#define RESIDUAL_CALC(_data_len) \
len = (_data_len); \
num = (count < len) ? count : len; \
*residual = count - num; \
if (count < len) *more = 1
/*-------------------------------------------------------------------*/
/* Assign a unique Message Id for this asynchronous I/O if needed */
/*-------------------------------------------------------------------*/
#if defined(OPTION_SCSI_TAPE)
#define INCREMENT_MESSAGEID(_dev) \
if ((_dev)->SIC_active) \
(_dev)->msgid++
#else
#define INCREMENT_MESSAGEID(_dev)
#endif // defined(OPTION_SCSI_TAPE)
/*-------------------------------------------------------------------*/
/* Functions defined in AWSTAPE.C */
/*-------------------------------------------------------------------*/
extern int open_awstape (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern void close_awstape (DEVBLK *dev);
extern int passedeot_awstape (DEVBLK *dev);
extern int rewind_awstape (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern int write_awsmark (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern int sync_awstape (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern int fsb_awstape (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern int bsb_awstape (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern int fsf_awstape (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern int bsf_awstape (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern int readhdr_awstape (DEVBLK *dev, off_t blkpos, AWSTAPE_BLKHDR *buf,
BYTE *unitstat, BYTE code);
extern int read_awstape (DEVBLK *dev, BYTE *buf,
BYTE *unitstat, BYTE code);
extern int write_awstape (DEVBLK *dev, BYTE *buf, U16 blklen,
BYTE *unitstat, BYTE code);
/*-------------------------------------------------------------------*/
/* Functions defined in FAKETAPE.C */
/*-------------------------------------------------------------------*/
extern int open_faketape (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern void close_faketape (DEVBLK *dev);
extern int passedeot_faketape (DEVBLK *dev);
extern int rewind_faketape (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern int write_fakemark (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern int sync_faketape (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern int fsb_faketape (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern int bsb_faketape (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern int fsf_faketape (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern int bsf_faketape (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern int readhdr_faketape (DEVBLK *dev, off_t blkpos,
U16* pprvblkl, U16* pcurblkl,
BYTE *unitstat, BYTE code);
extern int writehdr_faketape (DEVBLK *dev, off_t blkpos,
U16 prvblkl, U16 curblkl,
BYTE *unitstat, BYTE code);
extern int read_faketape (DEVBLK *dev, BYTE *buf,
BYTE *unitstat, BYTE code);
extern int write_faketape (DEVBLK *dev, BYTE *buf, U16 blklen,
BYTE *unitstat, BYTE code);
/*-------------------------------------------------------------------*/
/* Functions defined in HETTAPE.C */
/*-------------------------------------------------------------------*/
extern int open_het (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern void close_het (DEVBLK *dev);
extern int passedeot_het (DEVBLK *dev);
extern int rewind_het (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern int write_hetmark (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern int sync_het (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern int fsb_het (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern int bsb_het (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern int fsf_het (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern int bsf_het (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern int read_het (DEVBLK *dev, BYTE *buf,
BYTE *unitstat, BYTE code);
extern int write_het (DEVBLK *dev, BYTE *buf, U16 blklen,
BYTE *unitstat, BYTE code);
/*-------------------------------------------------------------------*/
/* Functions defined in OMATAPE.C */
/*-------------------------------------------------------------------*/
extern int open_omatape (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern void close_omatape (DEVBLK *dev);
extern void close_omatape2 (DEVBLK *dev);
extern int rewind_omatape (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern int fsb_omatape (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern int bsb_omatape (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern int fsf_omatape (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern int bsf_omatape (DEVBLK *dev, BYTE *unitstat, BYTE code);
extern int read_omadesc (DEVBLK *dev);
extern int fsb_omaheaders (DEVBLK *dev, OMATAPE_DESC *omadesc, BYTE *unitstat, BYTE code);
extern int fsb_omafixed (DEVBLK *dev, OMATAPE_DESC *omadesc, BYTE *unitstat, BYTE code);
extern int read_omaheaders (DEVBLK *dev, OMATAPE_DESC *omadesc, BYTE *buf, BYTE *unitstat, BYTE code);
extern int read_omafixed (DEVBLK *dev, OMATAPE_DESC *omadesc, BYTE *buf, BYTE *unitstat, BYTE code);
extern int read_omatext (DEVBLK *dev, OMATAPE_DESC *omadesc, BYTE *buf, BYTE *unitstat, BYTE code);
extern int read_omatape (DEVBLK *dev, BYTE *buf, BYTE *unitstat, BYTE code);
extern int readhdr_omaheaders (DEVBLK *dev, OMATAPE_DESC *omadesc,
long blkpos, S32 *pcurblkl,
S32 *pprvhdro, S32 *pnxthdro, BYTE *unitstat, BYTE code);
/*-------------------------------------------------------------------*/
/* Functions defined in SCSITAPE.C */
/*-------------------------------------------------------------------*/
// (see SCSITAPE.H)
/*
|| Tape ERA, HRA and SENSE constants
|| Note: For 3480/3490 tape drives HRA was an assumed function of the OS
|| For 3590 (NTP) tape drives HRA is no longer assumed. The labels
|| here are the 3480/3590 labels but the values are NTP values. See
|| sense byte 2 for additional information.
*/
/*-------------------------------------------------------------------*/
/* Host Recovery Action (HRA) (these are the 3590 codes */
/*-------------------------------------------------------------------*/
#define TAPE_HRA_PERMANENT_ERROR 0x00
#define TAPE_HRA_RETRY 0x80
#define TAPE_HRA_DDR 0x00 // Same as error for VT
#define TAPE_HRA_RESUME 0x40
#define TAPE_HRA_OPERATOR_INTERVENTION 0xC0
// Sense byte 0
#define TAPE_SNS0_CMDREJ 0x80 // Command Reject
#define TAPE_SNS0_INTVREQ 0x40 // Intervention Required
#define TAPE_SNS0_BUSCHK 0x20 // Bus-out Check
#define TAPE_SNS0_EQUIPCHK 0x10 // Equipment Check
#define TAPE_SNS0_DATACHK 0x08 // Data check
#define TAPE_SNS0_OVERRUN 0x04 // Overrun
#define TAPE_SNS0_DEFUNITCK 0x02 // Deferred Unit Check
#define TAPE_SNS0_ASSIGNED 0x01 // Assigned Elsewhere
// Sense byte 1
#define TAPE_SNS1_LOCFAIL 0x80 // Locate Failure
#define TAPE_SNS1_ONLINE 0x40 // Drive Online to CU
#define TAPE_SNS1_RSRVD 0x20 // Reserved
#define TAPE_SNS1_RCDSEQ 0x10 // Record Sequence Error
#define TAPE_SNS1_BOT 0x08 // Beginning of Tape
#define TAPE_SNS1_WRTMODE 0x04 // Write Mode
#define TAPE_SNS1_FILEPROT 0x02 // Write Protect
#define TAPE_SNS1_NOTCAPBL 0x01 // Not Capable
// Sense byte 2
/*
|| NTP SENSE BYTE 2
|| Log code is in byte 2(3-4), BRAC is in byte 2(0-1)
*/
#define TAPE_SNS2_NTP_BRAC_00_PERM_ERR 0x00 // BRAC 00 - PERM ERR
#define TAPE_SNS2_NTP_BRAC_01_CONTINUE 0x40 // BRAC 01 - Continue ( RESUME )
#define TAPE_SNS2_NTP_BRAC_10_REISSUE 0x80 // BRAC 10 - Reissue ( RETRY )
#define TAPE_SNS2_NTP_BRAC_11_DEFER_REISS 0xC0 // BRAC 11 - Deferred Reissue ( I/R ? )
#define TAPE_SNS2_NTP_LOG_CD0_NO_LOG 0x00
#define TAPE_SNS2_NTP_LOG_CD1_TEMP_OBR 0x08
#define TAPE_SNS2_NTP_LOG_CD2_PERM_OBR 0x10
#define TAPE_SNS2_NTP_LOG_CD3_A3 0x18
#define TAPE_SNS2_REPORTING_CHAN_PATH 0xF0 // Interface in the first 4 bits
#define TAPE_SNS2_REPORTING_CHAN_A 0x20 // Channel A Interface
#define TAPE_SNS2_REPORTING_CHAN_B 0x40 // Channel B Interface
#define TAPE_SNS2_REPORTING_CU 0x00 // Always 0 (ZERO) Bit 4
#define TAPE_SNS2_ACL_ACTIVE 0x04 // AutoLoader in SYS MODE and has Cart
#define TAPE_SNS2_SYNCMODE 0x02 // Tape Synchronous Mode
#define TAPE_SNS2_POSITION 0x01 // Tape Positioning
// Sense Byte 3
/*-------------------------------------------------------------------*/
/* Error Recovery Action (ERA) SENSE BYTE 3 */
/*-------------------------------------------------------------------*/
#define TAPE_ERA_UNSOLICITED_SENSE 0x00
#define TAPE_ERA_DATA_STREAMING_NOT_OPER 0x21
#define TAPE_ERA_PATH_EQUIPMENT_CHECK 0x22
#define TAPE_ERA_READ_DATA_CHECK 0x23
#define TAPE_ERA_LOAD_DISPLAY_CHECK 0x24
#define TAPE_ERA_WRITE_DATA_CHECK 0x25
#define TAPE_ERA_READ_OPPOSITE 0x26
#define TAPE_ERA_COMMAND_REJECT 0x27
#define TAPE_ERA_WRITE_ID_MARK_CHECK 0x28
#define TAPE_ERA_FUNCTION_INCOMPATIBLE 0x29
#define TAPE_ERA_UNSOL_ENVIRONMENTAL_DATA 0x2A
#define TAPE_ERA_ENVIRONMENTAL_DATA_PRESENT 0x2B
#define TAPE_ERA_PERMANENT_EQUIPMENT_CHECK 0x2C
#define TAPE_ERA_DATA_SECURE_ERASE_FAILURE 0x2D
#define TAPE_ERA_NOT_CAPABLE_BOT_ERROR 0x2E
#define TAPE_ERA_WRITE_PROTECTED 0x30
#define TAPE_ERA_TAPE_VOID 0x31
#define TAPE_ERA_TENSION_LOST 0x32
#define TAPE_ERA_LOAD_FAILURE 0x33
#define TAPE_ERA_UNLOAD_FAILURE 0x34
#define TAPE_ERA_MANUAL_UNLOAD 0x34
#define TAPE_ERA_DRIVE_EQUIPMENT_CHECK 0x35
#define TAPE_ERA_END_OF_DATA 0x36
#define TAPE_ERA_TAPE_LENGTH_ERROR 0x37
#define TAPE_ERA_PHYSICAL_END_OF_TAPE 0x38
#define TAPE_ERA_BACKWARD_AT_BOT 0x39
#define TAPE_ERA_DRIVE_SWITCHED_NOT_READY 0x3A
#define TAPE_ERA_DRIVE_RESET_BY_OPERATOR 0x3A
#define TAPE_ERA_MANUAL_REWIND_OR_UNLOAD 0x3B
#define TAPE_ERA_VOLUME_REMOVE_BY_OPERATOR 0x3B
#define TAPE_ERA_VOLUME_MANUALLY_UNLOADED 0x3C
#define TAPE_ERA_OVERRUN 0x40
#define TAPE_ERA_DEVICE_DEFERRED_ACCESS 0x40
#define TAPE_ERA_RECORD_SEQUENCE_ERROR 0x41
#define TAPE_ERA_BLOCK_ID_SEQUENCE_ERROR 0x41
#define TAPE_ERA_DEGRADED_MODE 0x42
#define TAPE_ERA_DRIVE_NOT_READY 0x43
#define TAPE_ERA_INTERVENTION_REQ 0x43
#define TAPE_ERA_LOCATE_BLOCK_FAILED 0x44
#define TAPE_ERA_DRIVE_ASSIGNED_ELSEWHERE 0x45
#define TAPE_ERA_DRIVE_NOT_ONLINE 0x46
#define TAPE_ERA_VOLUME_FENCED 0x47
#define TAPE_ERA_UNSOL_INFORMATIONAL_DATA 0x48
#define TAPE_ERA_CONTROLLING_COMP_RETRY_REQ 0x48
#define TAPE_ERA_BUS_OUT_CHECK 0x49
#define TAPE_ERA_BUS_OUT_PARITY 0x49
#define TAPE_ERA_CU_ERP_FAILURE 0x4A
#define TAPE_ERA_CU_AND_DRIVE_INCOMPATIBLE 0x4B
#define TAPE_ERA_RECOVERED_CHECKONE_FAILURE 0x4C
#define TAPE_ERA_RESETTING_EVENT 0x4D
#define TAPE_ERA_MAX_BLOCKSIZE_EXCEEDED 0x4E
#define TAPE_ERA_DEVICE_CONTROLLER_INCOMP 0x4F
#define TAPE_ERA_READ_BUFFERED_LOG 0x50
#define TAPE_ERA_BUFFERED_LOG_OVERFLOW 0x50
#define TAPE_ERA_BUFFERED_LOG_END_OF_VOLUME 0x51
#define TAPE_ERA_END_OF_VOLUME_PROCESSING 0x51
#define TAPE_ERA_END_OF_VOLUME_COMPLETE 0x52
#define TAPE_ERA_GLOBAL_COMMAND_INTERCEPT 0x53
#define TAPE_ERA_TEMP_CHNL_INTERFACE_ERROR 0x54
#define TAPE_ERA_PERM_CHNL_INTERFACE_ERROR 0x55
#define TAPE_ERA_CHNL_PROTOCOL_ERROR 0x56
#define TAPE_ERA_GLOBAL_STATUS_INTERCEPT 0x57
#define TAPE_ERA_ATTENTION_INTERCEPT 0x57
#define TAPE_ERA_TAPE_LENGTH_INCOMPAT 0x5A
#define TAPE_ERA_FORMAT_3480_XF_INCOMPAT 0x5B
#define TAPE_ERA_FORMAT_3480_2_XF_INCOMPAT 0x5C
#define TAPE_ERA_TAPE_LENGTH_VIOLATION 0x5D
#define TAPE_ERA_COMPACT_ALGORITHM_INCOMPAT 0x5E
/*
|| 3490/3590/NTP IN AN AUTOMATED LIBRARY SYSTEM
*/
#define TAPE_ERA_LIB_ATT_FAC_EQ_CHK 0x60
#define TAPE_ERA_LIB_MGR_OFFLINE_TO_SUBSYS 0x62
#define TAPE_ERA_LIB_MGR_CU_INCOMPAT 0x63
#define TAPE_ERA_LIB_VOLSER_IN_USE 0x64
#define TAPE_ERA_LIB_VOLUME_RESERVED 0x65
#define TAPE_ERA_LIB_VOLSER_NOT_IN_LIB 0x66
#define TAPE_ERA_LIB_CATEGORY_EMPTY 0x67
#define TAPE_ERA_LIB_ORDER_SEQ_CHK 0x68
#define TAPE_ERA_LIB_OUTPUT_STATIONS_FULL 0x69
#define TAPE_ERA_LIB_VOLUME_MISPLACED 0x6B
#define TAPE_ERA_LIB_MISPLACED_VOLUME_FOUND 0x6C
#define TAPE_ERA_LIB_DRIVE_NOT_UNLOADED 0x6D
#define TAPE_ERA_LIB_INACCESS_VOLUME_REST 0x6E
#define TAPE_ERA_LIB_OPTICS_FAILURE 0x6F
#define TAPE_ERA_LIB_MGR_EQ_CHK 0x70
#define TAPE_ERA_LIB_EQ_CHK 0x71
#define TAPE_ERA_LIB_NOT_CAP_MANUAL_MODE 0x72
#define TAPE_ERA_LIB_INTERVENTION_REQ 0x73
#define TAPE_ERA_LIB_INFORMATION_DATA 0x74
#define TAPE_ERA_LIB_VOLSER_INACCESS 0x75
#define TAPE_ERA_LIB_ALL_CELLS_FULL 0x76
#define TAPE_ERA_LIB_DUP_VOLSER_EJECTED 0x77
#define TAPE_ERA_LIB_DUP_VOLSER_LEFT_IN_STAT 0x78
#define TAPE_ERA_LIB_UNREADABLE_INVLD_VOLSER 0x79
#define TAPE_ERA_LIB_READ_STATISTICS 0x7A
#define TAPE_ERA_LIB_VOLUME_MAN_EJECTED 0x7B
#define TAPE_ERA_LIB_OUT_OF_CLEANER_VOLUMES 0x7C
#define TAPE_ERA_LIB_VOLUME_EXPORTED 0x7D
#define TAPE_ERA_LIB_CATEGORY_IN_USE 0x7F
#define TAPE_ERA_LIB_UNEXPECTED_VOLUME_EJECT 0x80
#define TAPE_ERA_LIB_IO_STATION_DOOR_OPEN 0x81
#define TAPE_ERA_LIB_MGR_PROG_EXCEPTION 0x82
#define TAPE_ERA_LIB_DRIVE_EXCEPTION 0x83
#define TAPE_ERA_LIB_DRIVE_FAILURE 0x84
#define TAPE_ERA_LIB_SMOKE_DETECTION_ALERT 0x85
#define TAPE_ERA_LIB_ALL_CATEGORYS_RESERVED 0x86
#define TAPE_ERA_LIB_DUP_VOLSER_ADDITION 0x87
#define TAPE_ERA_LIB_DAMAGE_CART_EJECTED 0x88
#define TAPE_ERA_LIB_VOLUME_INACCESSIBLE 0x91
/*
|| SENSE BYTE 3 for NTP (3590) TAPES
*/
#define TAPE_ERA_RAC_USE_BRAC 0xC0
#define TAPE_ERA_RAC_FENCE_DEVICE 0xC1
#define TAPE_ERA_RAC_FENCH_DEVICE_PATH 0xC2
#define TAPE_ERA_RAC_LONG_BUSY 0xC6
#define TAPE_ERA_RAC_READ_ALT 0xD2
// Sense byte 4
/*
|| SENSE BYTE 4 FOR TAPES
*/
#define TAPE_SNS4_3420_TAPE_INDICATE 0x20 // EOT FOUND
#define TAPE_SNS4_3480_FORMAT_MODE 0xC0
#define TAPE_SNS4_3480_FORMAT_MODE_XF 0x80
#define TAPE_SNS4_3490_FORMAT_MODE 0x00
#define TAPE_SNS4_3490_FORMAT_MODE_RSVD 0x40
#define TAPE_SNS4_3490_FORMAT_MODE_IDRC 0x80
#define TAPE_SNS4_3490_FORMAT_MODE_SPECIAL 0xC0
#define TAPE_SNS4_3480_HO_CHAN_LOG_BLK_ID 0x3F // 22-bits for BLK ID
// Sense byte 5
/*
|| SENSE BYTE 5 FOR TAPES
*/
#define TAPE_SNS5_3480_MO_CHAN_LOG_BLK_ID 0xFF
// Sense byte 6
/*
|| SENSE BYTE 6 FOR TAPES
*/
#define TAPE_SNS6_3480_LO_CHAN_LOG_BLK_ID 0xFF
/*
|| SENSE BYTES 4-5 FOR NTP BYTE 4 is Reason Code(RC) and 5 is Reason Qualifer Code(RQC)
*/
#define TAPE_SNS4_5_NTP_RC_UA_RQC_DEV_LOG 0x1110 // UNIT ATTENTION/Device Log
#define TAPE_SNS4_5_NTP_RC_LA_RQC_DEV_CLEANED 0x1211 // LIBRARY ATTENTION/Device CLEANED
#define TAPE_SNS4_5_NTP_RC_LA_RQC_DEV_QUIESCED 0x1212 // LIBRARY ATTENTION/Device QUIESCE
#define TAPE_SNS4_5_NTP_RC_LA_RQC_DEV_RESUMED 0x1213 // LIBRARY ATTENTION/Device RESUMED
#define TAPE_SNS4_5_NTP_RC_CMD_REJ 0x2000 // COMMAND REJECT
#define TAPE_SNS4_5_NTP_RC_PE_RQC_GBL_CMD 0x2230 // PROTECTION EXCEPTION/Global Command
#define TAPE_SNS4_5_NTP_RC_PE_RQC_GBL_STATUS 0x2231 // PROTECTION EXCEPTION/Global Status
#define TAPE_SNS4_5_NTP_RC_BE_RQC_EOV 0x3012 // BOUNDARY EXCEPTION/End of Volume
#define TAPE_SNS4_5_NTP_RC_DC_RQC_NO_FMT_BOV 0x5050 // DATA CHECK/No Formatting at BOV
#define TAPE_SNS4_5_NTP_RC_DC_RQC_NO_FMT 0x5051 // DATA CHECK/No Formatting Past BOV
#define TAPE_SNS4_5_NTP_RC_OE_RQC_MED_NOT_LD 0x4010 // OPERATIONAL EXCEPTION/Medium Not Loaded
#define TAPE_SNS4_5_NTP_RC_OE_RQC_DRV_NOT_RDY 0x4011 // OPERATIONAL EXCEPTION/Drive Not Ready
#define TAPE_SNS4_5_NTP_RC_OE_RQC_DEV_LONG_BSY 0x4012 // OPERATIONAL EXCEPTION/Device long busy
#define TAPE_SNS4_5_NTP_RC_OE_RQC_LDR_IR 0x4020 // OPERATIONAL EXCEPTION/Loader Interv Req'd
// Sense byte 7
/*
|| SENSE BYTE 7 FOR TAPES
*/
#define TAPE_SNS7_TAPE_SECURITY_ERASE_CMD 0x08
#define TAPE_SNS7_FMT_20_3480 0x20 // DRIVE AND CU ERROR INFORMATION
#define TAPE_SNS7_FMT_21_3480_READ_BUF_LOG 0x21 // BUFFERED LOG DATA WHEN NO IDRC is installed
#define TAPE_SNS7_FMT_30_3480_READ_BUF_LOG 0x30 // BUFFERED LOG DATA WHEN IDRC is installed
#define TAPE_SNS7_FMT_22_3480_EOV_STATS 0x22
#define TAPE_SNS7_FMT_23_ALT 0x23
#define TAPE_SNS7_FMT_50_NTP 0x50
#define TAPE_SNS7_FMT_51_NTP 0x51
#define TAPE_SNS7_FMT_70_3490 0x70
#define TAPE_SNS7_FMT_71_3490 0x71
#endif // __TAPEDEV_H__
hercules-3.13/fthreads.c 0000664 0001754 0001754 00000165264 13066015061 012165 0000000 0000000 /* FTHREADS.C (c) Copyright "Fish" (David B. Trout), 2001-2009 */
/* Fish's WIN32 version of pthreads */
////////////////////////////////////////////////////////////////////////////////////
// (c) Copyright "Fish" (David B. Trout), 2001-2009. Released under the Q Public License
// (http://www.hercules-390.org/herclic.html) as modifications to Hercules.
////////////////////////////////////////////////////////////////////////////////////
#include "hstdinc.h"
#define _FTHREADS_C_
#define _HUTIL_DLL_
#include "hercules.h"
#include "fthreads.h"
#if defined(OPTION_FTHREADS)
////////////////////////////////////////////////////////////////////////////////////
// Private internal fthreads structures...
typedef struct _tagFT_MUTEX // fthread "mutex" structure
{
CRITICAL_SECTION MutexLock; // (lock for accessing this data)
DWORD dwMutexMagic; // (magic number)
HANDLE hUnlockedEvent; // (signalled while NOT locked)
DWORD dwMutexType; // (type of mutex (normal, etc))
DWORD dwLockOwner; // (thread-id of who owns it)
int nLockedCount; // (#of times lock acquired)
}
FT_MUTEX, *PFT_MUTEX;
typedef struct _tagFT_COND_VAR // fthread "condition variable" structure
{
CRITICAL_SECTION CondVarLock; // (lock for accessing this data)
DWORD dwCondMagic; // (magic number)
HANDLE hSigXmitEvent; // set during signal transmission
HANDLE hSigRecvdEvent; // set once signal received by every-
// one that's supposed to receive it.
BOOL bBroadcastSig; // TRUE = "broadcast", FALSE = "signal"
int nNumWaiting; // #of threads waiting to receive signal
}
FT_COND_VAR, *PFT_COND_VAR;
////////////////////////////////////////////////////////////////////////////////////
// So we can tell whether our structures have been properly initialized or not...
#define FT_MUTEX_MAGIC 0x4D767478 // "Mutx" in ASCII
#define FT_COND_MAGIC 0x436F6E64 // "Cond" in ASCII
#define FT_ATTR_MAGIC 0x41747472 // "Attr" in ASCII
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
// Private internal fthreads functions...
static BOOL IsValidMutexType ( DWORD dwMutexType )
{
return (0
// || FTHREAD_MUTEX_DEFAULT == dwMutexType // (FTHREAD_MUTEX_RECURSIVE)
|| FTHREAD_MUTEX_RECURSIVE == dwMutexType
|| FTHREAD_MUTEX_ERRORCHECK == dwMutexType
// || FTHREAD_MUTEX_NORMAL == dwMutexType // (not currently supported)
);
}
////////////////////////////////////////////////////////////////////////////////////
static FT_MUTEX* MallocFT_MUTEX ( )
{
FT_MUTEX* pFT_MUTEX = (FT_MUTEX*) malloc ( sizeof ( FT_MUTEX ) );
if ( !pFT_MUTEX ) return NULL;
memset ( pFT_MUTEX, 0xCD, sizeof ( FT_MUTEX ) );
return pFT_MUTEX;
}
////////////////////////////////////////////////////////////////////////////////////
static BOOL InitializeFT_MUTEX
(
FT_MUTEX* pFT_MUTEX,
DWORD dwMutexType
)
{
// Note: UnlockedEvent created initially signalled
if ( !(pFT_MUTEX->hUnlockedEvent = MyCreateEvent ( NULL, TRUE, TRUE, NULL )) )
{
memset ( pFT_MUTEX, 0xCD, sizeof ( FT_MUTEX ) );
return FALSE;
}
MyInitializeCriticalSection ( &pFT_MUTEX->MutexLock );
pFT_MUTEX->dwMutexMagic = FT_MUTEX_MAGIC;
pFT_MUTEX->dwMutexType = dwMutexType;
pFT_MUTEX->dwLockOwner = 0;
pFT_MUTEX->nLockedCount = 0;
return TRUE;
}
////////////////////////////////////////////////////////////////////////////////////
static BOOL UninitializeFT_MUTEX
(
FT_MUTEX* pFT_MUTEX
)
{
if ( pFT_MUTEX->nLockedCount > 0 )
return FALSE; // (still in use)
ASSERT( IsEventSet ( pFT_MUTEX->hUnlockedEvent ) );
MyDeleteEvent ( pFT_MUTEX->hUnlockedEvent );
MyDeleteCriticalSection ( &pFT_MUTEX->MutexLock );
memset ( pFT_MUTEX, 0xCD, sizeof ( FT_MUTEX ) );
return TRUE;
}
////////////////////////////////////////////////////////////////////////////////////
static FT_COND_VAR* MallocFT_COND_VAR ( )
{
FT_COND_VAR* pFT_COND_VAR = (FT_COND_VAR*) malloc ( sizeof ( FT_COND_VAR ) );
if ( !pFT_COND_VAR ) return NULL;
memset ( pFT_COND_VAR, 0xCD, sizeof ( FT_COND_VAR ) );
return pFT_COND_VAR;
}
////////////////////////////////////////////////////////////////////////////////////
static BOOL InitializeFT_COND_VAR
(
FT_COND_VAR* pFT_COND_VAR
)
{
if ( ( pFT_COND_VAR->hSigXmitEvent = MyCreateEvent ( NULL, TRUE, FALSE, NULL ) ) )
{
// Note: hSigRecvdEvent created initially signaled
if ( ( pFT_COND_VAR->hSigRecvdEvent = MyCreateEvent ( NULL, TRUE, TRUE, NULL ) ) )
{
MyInitializeCriticalSection ( &pFT_COND_VAR->CondVarLock );
pFT_COND_VAR->dwCondMagic = FT_COND_MAGIC;
pFT_COND_VAR->bBroadcastSig = FALSE;
pFT_COND_VAR->nNumWaiting = 0;
return TRUE;
}
MyDeleteEvent ( pFT_COND_VAR->hSigXmitEvent );
}
memset ( pFT_COND_VAR, 0xCD, sizeof ( FT_COND_VAR ) );
return FALSE;
}
////////////////////////////////////////////////////////////////////////////////////
static BOOL UninitializeFT_COND_VAR
(
FT_COND_VAR* pFT_COND_VAR
)
{
if (0
|| pFT_COND_VAR->nNumWaiting
|| IsEventSet ( pFT_COND_VAR->hSigXmitEvent )
|| !IsEventSet ( pFT_COND_VAR->hSigRecvdEvent )
)
return FALSE;
MyDeleteEvent ( pFT_COND_VAR->hSigXmitEvent );
MyDeleteEvent ( pFT_COND_VAR->hSigRecvdEvent );
MyDeleteCriticalSection ( &pFT_COND_VAR->CondVarLock );
memset ( pFT_COND_VAR, 0xCD, sizeof ( FT_COND_VAR ) );
return TRUE;
}
////////////////////////////////////////////////////////////////////////////////////
static BOOL TryEnterFT_MUTEX
(
FT_MUTEX* pFT_MUTEX
)
{
BOOL bSuccess;
DWORD dwThreadId = GetCurrentThreadId();
if ( hostinfo.trycritsec_avail )
{
bSuccess = MyTryEnterCriticalSection ( &pFT_MUTEX->MutexLock );
if ( bSuccess )
{
pFT_MUTEX->nLockedCount++;
ASSERT( pFT_MUTEX->nLockedCount > 0 );
pFT_MUTEX->dwLockOwner = dwThreadId;
}
}
else
{
MyEnterCriticalSection ( &pFT_MUTEX->MutexLock );
ASSERT ( pFT_MUTEX->nLockedCount >= 0 );
bSuccess = ( pFT_MUTEX->nLockedCount <= 0 || pFT_MUTEX->dwLockOwner == dwThreadId );
if ( bSuccess )
{
pFT_MUTEX->nLockedCount++;
ASSERT ( pFT_MUTEX->nLockedCount > 0 );
pFT_MUTEX->dwLockOwner = dwThreadId;
MyResetEvent ( pFT_MUTEX->hUnlockedEvent );
}
MyLeaveCriticalSection ( &pFT_MUTEX->MutexLock );
}
return bSuccess;
}
////////////////////////////////////////////////////////////////////////////////////
static void EnterFT_MUTEX
(
FT_MUTEX* pFT_MUTEX
)
{
DWORD dwThreadId = GetCurrentThreadId();
if ( hostinfo.trycritsec_avail )
{
MyEnterCriticalSection ( &pFT_MUTEX->MutexLock );
pFT_MUTEX->dwLockOwner = dwThreadId;
pFT_MUTEX->nLockedCount++;
ASSERT ( pFT_MUTEX->nLockedCount > 0 );
}
else
{
for (;;)
{
MyEnterCriticalSection ( &pFT_MUTEX->MutexLock );
ASSERT ( pFT_MUTEX->nLockedCount >= 0 );
if ( pFT_MUTEX->nLockedCount <= 0 || pFT_MUTEX->dwLockOwner == dwThreadId ) break;
MyLeaveCriticalSection ( &pFT_MUTEX->MutexLock );
MyWaitForSingleObject ( pFT_MUTEX->hUnlockedEvent, INFINITE );
}
MyResetEvent ( pFT_MUTEX->hUnlockedEvent );
pFT_MUTEX->dwLockOwner = dwThreadId;
pFT_MUTEX->nLockedCount++;
ASSERT ( pFT_MUTEX->nLockedCount > 0 );
MyLeaveCriticalSection ( &pFT_MUTEX->MutexLock );
}
}
////////////////////////////////////////////////////////////////////////////////////
static void LeaveFT_MUTEX
(
FT_MUTEX* pFT_MUTEX
)
{
if ( hostinfo.trycritsec_avail )
{
ASSERT ( pFT_MUTEX->nLockedCount > 0 );
pFT_MUTEX->nLockedCount--;
if ( pFT_MUTEX->nLockedCount <= 0 )
pFT_MUTEX->dwLockOwner = 0;
MyLeaveCriticalSection ( &pFT_MUTEX->MutexLock );
}
else
{
MyEnterCriticalSection ( &pFT_MUTEX->MutexLock );
ASSERT ( pFT_MUTEX->nLockedCount > 0 );
pFT_MUTEX->nLockedCount--;
if ( pFT_MUTEX->nLockedCount <= 0 )
{
pFT_MUTEX->dwLockOwner = 0;
MySetEvent ( pFT_MUTEX->hUnlockedEvent );
}
MyLeaveCriticalSection ( &pFT_MUTEX->MutexLock );
}
}
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
// Now we get to the "meat" of fthreads...
//
// The below function atomically releases the caller's mutex and registers the fact
// that the caller wishes to wait on their condition variable (or rather the other
// way around: it first registers the fact that the caller wishes to wait on the
// condition by first acquiring the condition variable lock and then registering
// the wait and THEN afterwards (once the wait has been registered and condition
// variable lock acquired) releases the original mutex). This ensures that no one
// can "sneak a signal past us" from the time this function is called until we can
// manage to register the wait request since no signals can ever be sent while the
// condition variable is locked.
static int BeginWait
(
FT_COND_VAR* pFT_COND_VAR,
fthread_mutex_t* pFTUSER_MUTEX
)
{
int rc;
FT_MUTEX* pFT_MUTEX;
if (0
|| !pFT_COND_VAR // (invalid ptr)
|| pFT_COND_VAR -> dwCondMagic != FT_COND_MAGIC // (not initialized)
|| !pFTUSER_MUTEX // (invalid ptr)
|| pFTUSER_MUTEX-> dwMutexMagic != FT_MUTEX_MAGIC // (not initialized)
|| !(pFT_MUTEX = pFTUSER_MUTEX->hMutex) // (invalid ptr)
// || !pFT_MUTEX // (invalid ptr)
|| pFT_MUTEX -> dwMutexMagic != FT_MUTEX_MAGIC // (not initialized)
)
return RC(EINVAL);
if (0
|| pFT_MUTEX -> dwLockOwner != GetCurrentThreadId() // (mutex not owned)
|| pFT_MUTEX -> nLockedCount <= 0 // (mutex not locked)
)
return RC(EPERM);
// First, acquire the fthreads condition variable lock...
for (;;)
{
MyEnterCriticalSection ( &pFT_COND_VAR->CondVarLock );
// It is always safe to proceed if the prior signal was completely
// processed (received by everyone who was supposed to receive it)
if ( IsEventSet ( pFT_COND_VAR->hSigRecvdEvent ) )
break;
// Prior signal not completely received yet... Verify that it is
// still being transmitted...
ASSERT ( IsEventSet ( pFT_COND_VAR->hSigXmitEvent ) );
// If no one is currently waiting to receive [this signal not yet
// completely received and still being transmitted], then we can
// go ahead and receive it right now *regardless* of what type of
// signal it is ("signal" or "broadcast") since we're *obviously*
// the one who is supposed to receive it (since we ARE trying to
// wait on it after all and it IS being transmitted. The 'xmit'
// event is *always* turned off once everyone [who is *supposed*
// to receive the signal] *has* received the signal. Thus, since
// it's still being transmitted, that means *not* everyone who
// *should* receive it *has* received it yet, and thus we can be
// absolutely certain that we indeed *should* therefore receive it
// since we *are* after all waiting for it).
// Otherwise (prior signal not completely processed AND there are
// still others waiting to receive it too (as well as us)), then if
// it's a "broadcast" type signal, we can go ahead and receive that
// type of signal too as well (along with the others); we just came
// to the party a little bit late (but nevertheless in the nick of
// time!), that's all...
if ( !pFT_COND_VAR->nNumWaiting || pFT_COND_VAR->bBroadcastSig )
break;
// Otherwise it's a "signal" type signal (and not a broadcast type)
// that hasn't been completely received yet, meaning only ONE thread
// should be released. Thus, since there's already a thread (or more
// than one thread) already waiting/trying to receive it, we need to
// let [one of] THEM receive it and NOT US. Thus we go back to sleep
// and wait for the signal processing currently in progress to finish
// releasing the proper number of threads first. Only once that has
// happened can we then be allowed to try and catch whatever signal
// happens to come along next...
MyLeaveCriticalSection ( &pFT_COND_VAR->CondVarLock );
// (Programming Note: technically we should really be checking our
// return code from the below wait call too)
MyWaitForSingleObject ( pFT_COND_VAR->hSigRecvdEvent, INFINITE );
}
// Register the caller's wait request while we still have control
// over this condition variable...
pFT_COND_VAR->nNumWaiting++; // (register wait request)
// Now release the original mutex and thus any potential signalers...
// (but note that no signal can actually ever be sent per se until
// the condition variable which we currently have locked is first
// released, which gets done in the WaitForTransmission function).
if
(
(
rc = fthread_mutex_unlock
(
pFTUSER_MUTEX
)
)
!= 0
)
{
// Oops! Something went wrong. We couldn't release the original
// caller's original mutex. Since we've already registered their
// wait and already have the condition variable locked, we need
// to first de-register their wait and release the condition var-
// iable lock before returning our error (i.e. we essentially
// need to back out what we previously did just above).
logmsg("fthreads: BeginWait: fthread_mutex_unlock failed! rc=%d\n"
,rc );
pFT_COND_VAR->nNumWaiting--; // (de-register wait request)
MyLeaveCriticalSection ( &pFT_COND_VAR->CondVarLock );
return RC(rc);
}
// Our "begin-to-wait-on-condition-variable" task has been successfully
// completed. We have essentially atomically released the originally mutex
// and "begun our wait" on it (by registering the fact that there's someone
// wanting to wait on it). Return to OUR caller with the condition variable
// still locked (so no signals can be sent nor any threads released until
// our caller calls the below WaitForTransmission function)...
return RC(0); // (success)
}
////////////////////////////////////////////////////////////////////////////////////
// Wait for the condition variable in question to receive a transmission...
static int WaitForTransmission
(
FT_COND_VAR* pFT_COND_VAR,
struct timespec* pTimeTimeout // (NULL == INFINITE wait)
)
{
DWORD dwWaitRetCode, dwWaitMilliSecs;
// If the signal has already arrived (i.e. is still being transmitted)
// then there's no need to wait for it. Simply return success with the
// condition variable still locked...
if ( IsEventSet ( pFT_COND_VAR->hSigXmitEvent ) )
{
// There's no need to wait for the signal (transmission)
// to arrive because it's already been sent! Just return.
return 0; // (transmission received!)
}
// Our loop to wait for our transmission to arrive...
do
{
// Release condition var lock (so signal (transmission) can
// be sent) and then wait for the signal (transmission)...
MyLeaveCriticalSection ( &pFT_COND_VAR->CondVarLock );
// Need to calculate a timeout value if this is a
// timed condition wait as opposed to a normal wait...
// Note that we unfortunately need to do this on each iteration
// because Window's wait API requires a relative timeout value
// rather than an absolute TOD timeout value like pthreads...
if ( !pTimeTimeout )
{
dwWaitMilliSecs = INFINITE;
}
else
{
struct timeval TimeNow;
gettimeofday ( &TimeNow, NULL );
if (TimeNow.tv_sec > pTimeTimeout->tv_sec
||
(
TimeNow.tv_sec == pTimeTimeout->tv_sec
&&
(TimeNow.tv_usec * 1000) > pTimeTimeout->tv_nsec
)
)
{
dwWaitMilliSecs = 0;
}
else
{
dwWaitMilliSecs =
((pTimeTimeout->tv_sec - TimeNow.tv_sec) * 1000) +
((pTimeTimeout->tv_nsec - (TimeNow.tv_usec * 1000)) / 1000000);
}
}
// Finally we get to do the actual wait...
dwWaitRetCode =
MyWaitForSingleObject ( pFT_COND_VAR->hSigXmitEvent, dwWaitMilliSecs );
// A signal (transmission) has been sent; reacquire our condition var lock
// and receive the transmission (if it's still being transmitted that is)...
MyEnterCriticalSection ( &pFT_COND_VAR->CondVarLock );
// The "WAIT_OBJECT_0 == dwWaitRetCode && ..." clause in the below 'while'
// statement ensures that we will always break out of our wait loop whenever
// either a timeout occurs or our actual MyWaitForSingleObject call fails
// for any reason. As long as dwWaitRetCode is WAIT_OBJECT_0 though *AND*
// our event has still not been signaled yet, then we'll continue looping
// to wait for a signal (transmission) that we're supposed to receive...
// Also note that one might at first think/ask: "Gee, Fish, why do we need
// to check to see if the condition variable's "hSigXmitEvent" event has
// been set each time (via the 'IsEventSet' macro)? Shouldn't it *always*
// be set if the above MyWaitForSingleObject call returns??" The answer is
// of course no, it might NOT [still] be signaled. This is because whenever
// someone *does* happen to signal it, we will of course be released from
// our wait (the above MyWaitForSingleObject call) BUT... someone else who
// was also waiting for it may have managed to grab our condition variable
// lock before we could and they could have reset it. Thus we need to check
// it again each time.
}
while ( WAIT_OBJECT_0 == dwWaitRetCode && !IsEventSet( pFT_COND_VAR->hSigXmitEvent ) );
// Our signal (transmission) has either [finally] arrived or else we got
// tired of waiting for it (i.e. we timed out) or else there was an error...
if ( WAIT_OBJECT_0 == dwWaitRetCode ) return RC(0);
if ( WAIT_TIMEOUT == dwWaitRetCode ) return RC(ETIMEDOUT);
// Our wait failed! Something is VERY wrong! Maybe the condition variable
// was prematurely destroyed by someone? In any case there's not
// much we can do about it other than log the fact that it occurred and
// return the error back to the caller. Their wait request has, believe
// it or not, actually been completed (although not as they expected it
// would in all likelihood!)...
logmsg ( "fthreads: WaitForTransmission: MyWaitForSingleObject failed! dwWaitRetCode=%d (0x%8.8X)\n"
,dwWaitRetCode ,dwWaitRetCode );
return RC(EFAULT);
}
////////////////////////////////////////////////////////////////////////////////////
// Send a "signal" or "broadcast" to a condition variable...
static int QueueTransmission
(
FT_COND_VAR* pFT_COND_VAR,
BOOL bXmitType
)
{
if (0
|| !pFT_COND_VAR // (invalid ptr)
|| pFT_COND_VAR->dwCondMagic != FT_COND_MAGIC // (not initialized)
)
{
return RC(EINVAL); // (invalid parameters were passed)
}
// Wait for the condition variable to become free so we can begin transmitting
// our signal... If the condition variable is still "busy" (in use), then that
// means threads are still in the process of being released as a result of some
// prior signal (transmission). Thus we must wait until that work is completed
// first before we can send our new signal (transmission)...
for (;;)
{
MyEnterCriticalSection ( &pFT_COND_VAR->CondVarLock );
if ( IsEventSet ( pFT_COND_VAR->hSigRecvdEvent ) ) break;
MyLeaveCriticalSection ( &pFT_COND_VAR->CondVarLock );
MyWaitForSingleObject ( pFT_COND_VAR->hSigRecvdEvent, INFINITE );
}
// Turn on our transmitter... (i.e. start transmitting our "signal" to
// all threads who might be waiting to receive it (if there are any)...
// If no one has registered their interest in receiving any transmissions
// associated with this particular condition variable, then they are unfor-
// tunately a little too late in doing so because we're ready to start our
// transmission right now! If there's no one to receive our transmission,
// then it simply gets lost (i.e. a "missed signal" situation has essenti-
// ally occurred), but then that's not our concern here; our only concern
// here is to transmit the signal (transmission) and nothing more. Tough
// beans if there's no one listening to receive it...
if ( pFT_COND_VAR->nNumWaiting ) // (anyone interested?)
{
pFT_COND_VAR->bBroadcastSig = bXmitType; // (yep! set xmit type)
MySetEvent ( pFT_COND_VAR->hSigXmitEvent ); // (turn on transmitter)
MyResetEvent ( pFT_COND_VAR->hSigRecvdEvent ); // (serialize reception)
}
MyLeaveCriticalSection ( &pFT_COND_VAR->CondVarLock );
return RC(0);
}
////////////////////////////////////////////////////////////////////////////////////
// A thread has been released as a result of someone's signal or broadcast...
static void ReceiveXmission
(
FT_COND_VAR* pFT_COND_VAR
)
{
// If we were the only ones supposed to receive the transmission, (or
// if no one remains to receive any transmissions), then turn off the
// transmitter (i.e. stop sending the signal) and indicate that it has
// been completely received all interested parties (i.e. by everyone
// who was supposed to receive it)...
pFT_COND_VAR->nNumWaiting--; // (de-register wait since transmission
// has been successfully received now)
// Determine whether any more waiters (threads) should also receive
// this transmission (i.e. also be released) or whether we should
// reset (turn off) our transmitter so as to not release any other
// thread(s) besides ourselves...
if (0
|| !pFT_COND_VAR->bBroadcastSig // ("signal" == only us)
|| pFT_COND_VAR->nNumWaiting <= 0 // (no one else == only us)
)
{
MyResetEvent ( pFT_COND_VAR->hSigXmitEvent ); // (turn off transmitter)
MySetEvent ( pFT_COND_VAR->hSigRecvdEvent ); // (transmission complete)
}
}
////////////////////////////////////////////////////////////////////////////////////
// The following function is called just before returning back to the caller. It
// first releases the condition variable lock (since we're now done with it) and
// then reacquires the caller's original mutex before returning [back to the caller].
// We MUST do things in that order! 1) release our condition variable lock, and THEN
// 2) try to reacquire the caller's original mutex. Otherwise a deadlock could occur!
// If we still had the condition variable locked before trying to acquire the caller's
// original mutex, we could easily become blocked if some other thread still already
// owned (had locked) the caller's mutex. We would then be unable to ever release our
// condition variable lock until whoever had the mutex locked first released it, but
// they would never be able to release it because we still had the condition variable
// still locked! Recall that upon entry to a wait call (see previous BeginWait function)
// we acquire the condition variable lock *first* (in order to register the caller's
// wait) before releasing their mutex. Thus, we MUST therefore release our condition
// variable lock FIRST and THEN try reacquiring their original mutex before returning.
static int ReturnFromWait
(
FT_COND_VAR* pFT_COND_VAR,
fthread_mutex_t* pFTUSER_MUTEX,
int nRetCode
)
{
int rc;
FT_MUTEX* pFT_MUTEX;
if (0
|| !pFT_COND_VAR // (invalid ptr)
|| pFT_COND_VAR -> dwCondMagic != FT_COND_MAGIC // (not initialized)
|| !pFTUSER_MUTEX // (invalid ptr)
|| pFTUSER_MUTEX-> dwMutexMagic != FT_MUTEX_MAGIC // (not initialized)
|| !(pFT_MUTEX = pFTUSER_MUTEX->hMutex) // (invalid ptr)
// || !pFT_MUTEX // (invalid ptr)
|| pFT_MUTEX -> dwMutexMagic != FT_MUTEX_MAGIC // (not initialized)
)
return RC(EINVAL);
// (let other threads access this condition variable now...)
MyLeaveCriticalSection ( &pFT_COND_VAR->CondVarLock );
// (reacquire original mutex before returning back to the original caller...)
if
(
(
rc = fthread_mutex_lock
(
pFTUSER_MUTEX
)
)
!= 0
)
{
// Oops! We were unable to reacquire the caller's original mutex! This
// is actually a catastrophic type of error! The caller expects their
// mutex to still be owned (locked) by themselves upon return, but we
// were unable to reacquire it for them! Unfortunately there's nothing
// we can do about this; the system is essentially hosed at this point.
// Just log the fact that something went wrong and return.
logmsg("fthreads: ReturnFromWait: fthread_mutex_lock failed! rc=%d\n"
,rc );
return RC(rc); // (what went wrong)
}
// Return to caller with the requested return code. (The caller passes to us
// the return code they wish to pass back to the original caller, so we just
// return that same return code back to OUR caller (so they can then pass it
// back to the original fthreads caller)).
return RC(nRetCode); // (as requested)
}
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
// Threading functions...
LIST_ENTRY ThreadListHead; // head list entry of list of joinable threads
CRITICAL_SECTION ThreadListLock; // lock for accessing list of joinable threads
#define LockThreadsList() EnterCriticalSection ( &ThreadListLock )
#define UnlockThreadsList() LeaveCriticalSection ( &ThreadListLock )
////////////////////////////////////////////////////////////////////////////////////
// internal joinable thread information
typedef struct _tagFTHREAD
{
LIST_ENTRY ThreadListLink; // (links entries together in a chain)
DWORD dwThreadID; // (thread-id)
HANDLE hThreadHandle; // (Win32 thread handle)
BOOL bJoinable; // (whether thread is joinable or detached)
int nJoinedCount; // (#of threads that did join on this one)
void* ExitVal; // (saved thread exit value)
jmp_buf JumpBuf; // (jump buffer for fthread_exit)
}
FTHREAD, *PFTHREAD;
////////////////////////////////////////////////////////////////////////////////////
// (Note: returns with thread list lock still held if found; not held if not found)
static FTHREAD* FindFTHREAD ( DWORD dwThreadID )
{
FTHREAD* pFTHREAD;
LIST_ENTRY* pListEntry;
LockThreadsList(); // (acquire thread list lock)
pListEntry = ThreadListHead.Flink;
while ( pListEntry != &ThreadListHead )
{
pFTHREAD = CONTAINING_RECORD ( pListEntry, FTHREAD, ThreadListLink );
pListEntry = pListEntry->Flink;
if ( pFTHREAD->dwThreadID != dwThreadID )
continue;
return pFTHREAD; // (return with thread list lock still held)
}
UnlockThreadsList(); // (release thread list lock)
return NULL; // (not found)
}
////////////////////////////////////////////////////////////////////////////////////
typedef struct _ftCallThreadParms
{
PFT_THREAD_FUNC pfnTheirThreadFunc;
void* pvTheirThreadArgs;
char* pszTheirThreadName;
FTHREAD* pFTHREAD;
}
FT_CALL_THREAD_PARMS;
//----------------------------------------------------------------------------------
static DWORD __stdcall FTWin32ThreadFunc
(
void* pMyArgs
)
{
FT_CALL_THREAD_PARMS* pCallTheirThreadParms;
PFT_THREAD_FUNC pfnTheirThreadFunc;
void* pvTheirThreadArgs;
FTHREAD* pFTHREAD;
pCallTheirThreadParms = (FT_CALL_THREAD_PARMS*) pMyArgs;
pfnTheirThreadFunc = pCallTheirThreadParms->pfnTheirThreadFunc;
pvTheirThreadArgs = pCallTheirThreadParms->pvTheirThreadArgs;
pFTHREAD = pCallTheirThreadParms->pFTHREAD;
// PROGRAMMING NOTE: -1 == "current calling thread"
SET_THREAD_NAME_ID ( -1, pCallTheirThreadParms->pszTheirThreadName );
free ( pCallTheirThreadParms );
if ( setjmp ( pFTHREAD->JumpBuf ) == 0 )
pFTHREAD->ExitVal = pfnTheirThreadFunc ( pvTheirThreadArgs );
LockThreadsList();
if ( !pFTHREAD->bJoinable )
{
// If we are not a joinable thread, we must free our
// own resources ourselves, but ONLY IF the 'joined'
// count is zero. If the 'joined' count is NOT zero,
// then, however it occurred, there is still someone
// waiting in the join function for us to exit, and
// thus, we cannot free our resources at this time
// (since the thread that did the join and which is
// waiting for us to exit still needs access to our
// resources). In such a situation the actual freeing
// of resources is deferred and will be done by the
// join function itself whenever it's done with them.
if ( pFTHREAD->nJoinedCount <= 0 )
{
CloseHandle ( pFTHREAD->hThreadHandle );
RemoveListEntry ( &pFTHREAD->ThreadListLink );
free ( pFTHREAD );
}
}
UnlockThreadsList();
MyExitThread ( 0 );
return 0; // (make compiler happy)
}
////////////////////////////////////////////////////////////////////////////////////
// Create a new thread...
DLL_EXPORT
int fthread_create
(
fthread_t* pdwThreadID,
fthread_attr_t* pThreadAttr,
PFT_THREAD_FUNC pfnThreadFunc,
void* pvThreadArgs,
char* pszThreadName
)
{
static BOOL bDidInit = FALSE;
FT_CALL_THREAD_PARMS* pCallTheirThreadParms;
size_t nStackSize;
int nDetachState;
FTHREAD* pFTHREAD;
HANDLE hThread;
DWORD dwThreadID;
if ( !bDidInit )
{
bDidInit = TRUE;
InitializeListHead ( &ThreadListHead );
InitializeCriticalSection ( &ThreadListLock );
}
if (0
|| !pdwThreadID
|| !pfnThreadFunc
)
return RC(EINVAL);
if ( pThreadAttr )
{
if ( pThreadAttr->dwAttrMagic != FT_ATTR_MAGIC ||
( pThreadAttr->nDetachState != FTHREAD_CREATE_DETACHED &&
pThreadAttr->nDetachState != FTHREAD_CREATE_JOINABLE ) )
return RC(EINVAL);
nStackSize = pThreadAttr->nStackSize;
nDetachState = pThreadAttr->nDetachState;
}
else
{
nStackSize = 0;
nDetachState = FTHREAD_CREATE_DEFAULT;
}
pCallTheirThreadParms = (FT_CALL_THREAD_PARMS*)
malloc ( sizeof ( FT_CALL_THREAD_PARMS ) );
if ( !pCallTheirThreadParms )
{
logmsg("fthread_create: malloc(FT_CALL_THREAD_PARMS) failed\n");
return RC(ENOMEM); // (out of memory)
}
pFTHREAD = (FTHREAD*)
malloc ( sizeof( FTHREAD ) );
if ( !pFTHREAD )
{
logmsg("fthread_create: malloc(FTHREAD) failed\n");
free ( pCallTheirThreadParms );
return RC(ENOMEM); // (out of memory)
}
pCallTheirThreadParms->pfnTheirThreadFunc = pfnThreadFunc;
pCallTheirThreadParms->pvTheirThreadArgs = pvThreadArgs;
pCallTheirThreadParms->pszTheirThreadName = pszThreadName;
pCallTheirThreadParms->pFTHREAD = pFTHREAD;
InitializeListLink(&pFTHREAD->ThreadListLink);
pFTHREAD->dwThreadID = 0;
pFTHREAD->hThreadHandle = NULL;
pFTHREAD->bJoinable = ((FTHREAD_CREATE_JOINABLE == nDetachState) ? (TRUE) : (FALSE));
pFTHREAD->nJoinedCount = 0;
pFTHREAD->ExitVal = NULL;
LockThreadsList();
hThread =
MyCreateThread ( NULL, nStackSize, FTWin32ThreadFunc, pCallTheirThreadParms, 0, &dwThreadID );
if ( !hThread )
{
UnlockThreadsList();
logmsg("fthread_create: MyCreateThread failed\n");
free ( pCallTheirThreadParms );
free ( pFTHREAD );
return RC(EAGAIN); // (unable to obtain required resources)
}
pFTHREAD->hThreadHandle = hThread;
pFTHREAD->dwThreadID = dwThreadID;
*pdwThreadID = dwThreadID;
InsertListHead ( &ThreadListHead, &pFTHREAD->ThreadListLink );
UnlockThreadsList();
return RC(0);
}
////////////////////////////////////////////////////////////////////////////////////
// Exit from a thread...
DLL_EXPORT
void fthread_exit
(
void* ExitVal
)
{
FTHREAD* pFTHREAD;
VERIFY ( pFTHREAD = FindFTHREAD ( GetCurrentThreadId() ) );
pFTHREAD->ExitVal = ExitVal;
UnlockThreadsList();
longjmp ( pFTHREAD->JumpBuf, 1 );
}
////////////////////////////////////////////////////////////////////////////////////
// Join a thread (i.e. wait for a thread's termination)...
DLL_EXPORT
int fthread_join
(
fthread_t dwThreadID,
void** pExitVal
)
{
HANDLE hThread;
FTHREAD* pFTHREAD;
if ( GetCurrentThreadId() == dwThreadID )
return RC(EDEADLK); // (can't join self!)
if ( !(pFTHREAD = FindFTHREAD ( dwThreadID ) ) )
return RC(ESRCH); // (thread not found)
// (Note: threads list lock still held at this point
// since thread was found...)
if ( !pFTHREAD->bJoinable )
{
UnlockThreadsList();
return RC(EINVAL); // (not a joinable thread)
}
ASSERT ( pFTHREAD->nJoinedCount >= 0 );
pFTHREAD->nJoinedCount++;
hThread = pFTHREAD->hThreadHandle;
// Wait for thread to exit...
UnlockThreadsList();
{
WaitForSingleObject ( hThread, INFINITE );
}
LockThreadsList();
if ( pExitVal )
*pExitVal = pFTHREAD->ExitVal; // (pass back thread's exit value)
ASSERT ( pFTHREAD->nJoinedCount > 0 );
pFTHREAD->nJoinedCount--;
// If this is the last thread to be resumed after having been suspended
// (as a result of doing the join), then we need to do the detach (i.e.
// to free resources), BUT ONLY IF the detach for the thread in question
// has already been done by someone (which can be determined by virtue of
// the "joinable" flag having already been changed back to non-joinable).
// The idea here is that the 'detach' function purposely does not free
// the resources unless the 'joined' count is zero. If the joined count
// is NOT zero whenever the detach function is called, then the resources
// cannot be freed since there's still a thread waiting to be resumed
// from its join (perhaps us!), and it obviously still needs access to
// the resources in question. Thus, in such a situation (i.e. there still
// being a thread remaining to be woken up from its join when the detach
// is done), the freeing of resources normally done by the detach function
// is deferred so that WE can do the resource freeing ourselves once we
// are done with them.
if ( !pFTHREAD->bJoinable && pFTHREAD->nJoinedCount <= 0 )
{
CloseHandle ( pFTHREAD->hThreadHandle );
RemoveListEntry ( &pFTHREAD->ThreadListLink );
free ( pFTHREAD );
}
UnlockThreadsList();
return RC(0);
}
////////////////////////////////////////////////////////////////////////////////////
// Detach a thread (i.e. ignore a thread's termination)...
DLL_EXPORT
int fthread_detach
(
fthread_t dwThreadID
)
{
FTHREAD* pFTHREAD;
if ( !( pFTHREAD = FindFTHREAD ( dwThreadID ) ) )
return RC(ESRCH); // (thread not found)
// (Note: threads list lock still held at this point
// since thread was found...)
if ( !pFTHREAD->bJoinable )
{
UnlockThreadsList();
return RC(EINVAL); // (not a joinable thread)
}
// If the thread has not yet exited, then IT will free its
// own resources itself whenever it eventually does exit by
// virtue of our changing it to a non-joinable thread type.
pFTHREAD->bJoinable = FALSE; // (indicate detach was done)
// Otherwise we need to free its resources ourselves since
// it obviously can't (since it has already exited).
// Note that we cannot free the resources ourselves even if the
// thread has already exited if there are still other threads
// waiting to be woken up from their own join (since they will
// still need to have access to the resources). In other words,
// even if the thread has already exited (and thus it would seem
// that our freeing the resources would be the proper thing to
// do), we CANNOT do so if the 'join' count is non-zero.
// In such a situation (the join count being non-zero indicating
// there is still another thread waiting to be resumed from its
// own join), we simply defer the actual freeing of resources to
// the thread still waiting to be woken up from its join. Whenever
// it does eventually wake up from its join, it will free the
// resources for us, as long as we remember to reset the 'joinable'
// flag back to non-joinable (which we've already done just above).
if ( IsEventSet ( pFTHREAD->hThreadHandle ) && pFTHREAD->nJoinedCount <= 0 )
{
CloseHandle ( pFTHREAD->hThreadHandle );
RemoveListEntry ( &pFTHREAD->ThreadListLink );
free ( pFTHREAD );
}
UnlockThreadsList();
return RC(0);
}
////////////////////////////////////////////////////////////////////////////////////
// Initialize a "thread attribute"...
DLL_EXPORT
int fthread_attr_init
(
fthread_attr_t* pThreadAttr
)
{
if ( !pThreadAttr )
return RC(EINVAL); // (invalid ptr)
if ( FT_ATTR_MAGIC == pThreadAttr->dwAttrMagic )
return RC(EBUSY); // (already initialized)
pThreadAttr->dwAttrMagic = FT_ATTR_MAGIC;
pThreadAttr->nDetachState = FTHREAD_CREATE_DEFAULT;
pThreadAttr->nStackSize = 0;
return RC(0);
}
////////////////////////////////////////////////////////////////////////////////////
// Destroy a "thread attribute"...
DLL_EXPORT
int fthread_attr_destroy
(
fthread_attr_t* pThreadAttr
)
{
if ( !pThreadAttr )
return RC(EINVAL); // (invalid ptr)
if ( FT_ATTR_MAGIC != pThreadAttr->dwAttrMagic )
return RC(EINVAL); // (not initialized)
pThreadAttr->dwAttrMagic = 0;
pThreadAttr->nDetachState = 0;
pThreadAttr->nStackSize = 0;
return RC(0);
}
////////////////////////////////////////////////////////////////////////////////////
// Set a thread's "detachstate" attribute...
DLL_EXPORT
int fthread_attr_setdetachstate
(
fthread_attr_t* pThreadAttr,
int nDetachState
)
{
if ( !pThreadAttr )
return RC(EINVAL); // (invalid ptr)
if ( FT_ATTR_MAGIC != pThreadAttr->dwAttrMagic )
return RC(EINVAL); // (not initialized)
if ( FTHREAD_CREATE_DETACHED != nDetachState &&
FTHREAD_CREATE_JOINABLE != nDetachState )
return RC(EINVAL); // (invalid detach state)
pThreadAttr->nDetachState = nDetachState;
return RC(0);
}
////////////////////////////////////////////////////////////////////////////////////
// Retrieve a thread's "detachstate" attribute...
DLL_EXPORT
int fthread_attr_getdetachstate
(
const fthread_attr_t* pThreadAttr,
int* pnDetachState
)
{
if ( !pThreadAttr || !pnDetachState )
return RC(EINVAL); // (invalid ptr)
if ( FT_ATTR_MAGIC != pThreadAttr->dwAttrMagic )
return RC(EINVAL); // (not initialized)
if ( FTHREAD_CREATE_DETACHED != pThreadAttr->nDetachState &&
FTHREAD_CREATE_JOINABLE != pThreadAttr->nDetachState )
return RC(EINVAL); // (invalid detach state)
*pnDetachState = pThreadAttr->nDetachState;
return RC(0);
}
////////////////////////////////////////////////////////////////////////////////////
// Set a thread's initial stack size...
DLL_EXPORT
int fthread_attr_setstacksize
(
fthread_attr_t* pThreadAttr,
size_t nStackSize
)
{
if ( !pThreadAttr )
return RC(EINVAL); // (invalid ptr)
if ( FT_ATTR_MAGIC != pThreadAttr->dwAttrMagic )
return RC(EINVAL); // (not initialized)
pThreadAttr->nStackSize = nStackSize;
return RC(0);
}
////////////////////////////////////////////////////////////////////////////////////
// Retrieve a thread's initial stack size...
DLL_EXPORT
int fthread_attr_getstacksize
(
const fthread_attr_t* pThreadAttr,
size_t* pnStackSize
)
{
if ( !pThreadAttr || !pnStackSize )
return RC(EINVAL); // (invalid ptr)
if ( FT_ATTR_MAGIC != pThreadAttr->dwAttrMagic )
return RC(EINVAL); // (not initialized)
*pnStackSize = pThreadAttr->nStackSize;
return RC(0);
}
////////////////////////////////////////////////////////////////////////////////////
// (thread signalling not [currently] supported (yet); always returns ENOTSUP...)
DLL_EXPORT
int fthread_kill // FIXME: TODO:
(
int dummy1,
int dummy2
)
{
UNREFERENCED ( dummy1 );
UNREFERENCED ( dummy2 );
return RC(ENOTSUP);
}
////////////////////////////////////////////////////////////////////////////////////
// Return thread-id...
DLL_EXPORT
fthread_t fthread_self ()
{
return GetCurrentThreadId();
}
////////////////////////////////////////////////////////////////////////////////////
// Compare thread-ids...
DLL_EXPORT
int fthread_equal
(
fthread_t pdwThreadID_1,
fthread_t pdwThreadID_2
)
{
return ( pdwThreadID_1 == pdwThreadID_2 );
}
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
// Initialize a lock...
DLL_EXPORT
int fthread_mutex_init
(
fthread_mutex_t* pFTUSER_MUTEX,
const fthread_mutexattr_t* pFT_MUTEX_ATTR
)
{
DWORD dwMutexType = 0;
if ( !pFTUSER_MUTEX )
return RC(EINVAL); // (invalid mutex ptr)
if ( FT_MUTEX_MAGIC == pFTUSER_MUTEX->dwMutexMagic )
return RC(EBUSY); // (mutex already initialized)
if ( pFT_MUTEX_ATTR && !IsValidMutexType ( dwMutexType = *pFT_MUTEX_ATTR ) )
return RC(EINVAL); // (invalid mutex attr ptr or mutex attr type)
if ( !(pFTUSER_MUTEX->hMutex = MallocFT_MUTEX()) )
return RC(ENOMEM); // (out of memory)
if ( !InitializeFT_MUTEX
(
pFTUSER_MUTEX->hMutex,
pFT_MUTEX_ATTR ? dwMutexType : FTHREAD_MUTEX_DEFAULT
))
{
free ( pFTUSER_MUTEX->hMutex );
pFTUSER_MUTEX->dwMutexMagic = 0;
pFTUSER_MUTEX->hMutex = NULL;
return RC(EAGAIN); // (unable to obtain required resources)
}
pFTUSER_MUTEX->dwMutexMagic = FT_MUTEX_MAGIC;
return RC(0);
}
////////////////////////////////////////////////////////////////////////////////////
// Destroy a lock...
DLL_EXPORT
int fthread_mutex_destroy
(
fthread_mutex_t* pFTUSER_MUTEX
)
{
if ( !pFTUSER_MUTEX )
return RC(EINVAL); // (invalid ptr)
if ( FT_MUTEX_MAGIC != pFTUSER_MUTEX->dwMutexMagic )
return RC(EINVAL); // (not initialized)
if ( !UninitializeFT_MUTEX
(
pFTUSER_MUTEX->hMutex
))
return RC(EBUSY); // (still in use)
free ( pFTUSER_MUTEX->hMutex );
pFTUSER_MUTEX->dwMutexMagic = 0;
pFTUSER_MUTEX->hMutex = NULL;
return RC(0);
}
////////////////////////////////////////////////////////////////////////////////////
// Try to lock a "mutex"...
DLL_EXPORT
int fthread_mutex_trylock
(
fthread_mutex_t* pFTUSER_MUTEX
)
{
if ( !pFTUSER_MUTEX )
return RC(EINVAL); // (invalid ptr)
if ( FT_MUTEX_MAGIC != pFTUSER_MUTEX->dwMutexMagic )
return RC(EINVAL); // (not initialized)
// Try to acquire the requested mutex...
if
(
!TryEnterFT_MUTEX
(
pFTUSER_MUTEX->hMutex
)
)
// We could not acquire the mutex; return 'busy'...
return RC(EBUSY);
// We successfully acquired the mutex... If the mutex type is recursive,
// or, if not recursive (i.e. error-check), if this was the first/initial
// lock on the mutex, then return success...
if (0
|| FTHREAD_MUTEX_RECURSIVE == ((FT_MUTEX*)pFTUSER_MUTEX->hMutex)->dwMutexType
|| ((FT_MUTEX*)pFTUSER_MUTEX->hMutex)->nLockedCount <= 1
)
return RC(0);
ASSERT ( FTHREAD_MUTEX_ERRORCHECK == ((FT_MUTEX*)pFTUSER_MUTEX->hMutex)->dwMutexType
&& ((FT_MUTEX*)pFTUSER_MUTEX->hMutex)->nLockedCount > 1 );
// The mutex type is error-check and we already previously had the mutex locked
// before (i.e. this was the *second* time we acquired this same mutex). Return
// 'busy' after first releasing the mutex once (to decrement the locked count
// back down to what it was (i.e. 1 (one))).
LeaveFT_MUTEX
(
pFTUSER_MUTEX->hMutex
);
return RC(EBUSY);
}
////////////////////////////////////////////////////////////////////////////////////
// Lock a "mutex"...
DLL_EXPORT
int fthread_mutex_lock
(
fthread_mutex_t* pFTUSER_MUTEX
)
{
if ( !pFTUSER_MUTEX )
return RC(EINVAL); // (invalid ptr)
if ( FT_MUTEX_MAGIC != pFTUSER_MUTEX->dwMutexMagic )
return RC(EINVAL); // (not initialized)
// Try to acquire the requested mutex...
if
(
!TryEnterFT_MUTEX
(
pFTUSER_MUTEX->hMutex
)
)
{
// We could not acquire the mutex. This means someone already owns the mutex,
// so just do a normal acquire on the mutex. Both recursive and error-check
// types will block until such time as the mutex is successfully acquired...
EnterFT_MUTEX
(
pFTUSER_MUTEX->hMutex
);
return RC(0);
}
// We successfully acquired the mutex... If the mutex type is recursive,
// or, if not recursive (i.e. error-check), if this was the first/initial
// lock on the mutex, then return success...
if (0
|| FTHREAD_MUTEX_RECURSIVE == ((FT_MUTEX*)pFTUSER_MUTEX->hMutex)->dwMutexType
|| ((FT_MUTEX*)pFTUSER_MUTEX->hMutex)->nLockedCount <= 1
)
return RC(0);
ASSERT ( FTHREAD_MUTEX_ERRORCHECK == ((FT_MUTEX*)pFTUSER_MUTEX->hMutex)->dwMutexType
&& ((FT_MUTEX*)pFTUSER_MUTEX->hMutex)->nLockedCount > 1 );
// The mutex type is error-check and we already previously had the mutex locked
// before (i.e. this was the *second* time we acquired this same mutex). Return
// 'deadlock' after first releasing the mutex once (to decrement the locked count
// back down to what it was (i.e. 1 (one))).
LeaveFT_MUTEX
(
pFTUSER_MUTEX->hMutex
);
return RC(EDEADLK);
}
////////////////////////////////////////////////////////////////////////////////////
// Unlock a "mutex"...
DLL_EXPORT
int fthread_mutex_unlock
(
fthread_mutex_t* pFTUSER_MUTEX
)
{
if (0
|| !pFTUSER_MUTEX // (invalid ptr)
|| FT_MUTEX_MAGIC != pFTUSER_MUTEX->dwMutexMagic // (not initialized)
)
return RC(EINVAL);
if (0
|| GetCurrentThreadId() != ((PFT_MUTEX)pFTUSER_MUTEX->hMutex)->dwLockOwner // (not owned)
|| ((PFT_MUTEX)pFTUSER_MUTEX->hMutex)->nLockedCount <= 0 // (not locked)
)
return RC(EPERM);
ASSERT ( ((FT_MUTEX*)pFTUSER_MUTEX->hMutex)->nLockedCount <= 1
|| FTHREAD_MUTEX_RECURSIVE == ((FT_MUTEX*)pFTUSER_MUTEX->hMutex)->dwMutexType );
LeaveFT_MUTEX
(
pFTUSER_MUTEX->hMutex
);
return RC(0);
}
////////////////////////////////////////////////////////////////////////////////////
// Initialize a "condition"...
DLL_EXPORT
int fthread_cond_init
(
fthread_cond_t* pFT_COND_VAR
)
{
if ( !pFT_COND_VAR )
return RC(EINVAL); // (invalid ptr)
if ( FT_COND_MAGIC == pFT_COND_VAR->dwCondMagic )
return RC(EBUSY); // (already initialized)
if ( !(pFT_COND_VAR->hCondVar = MallocFT_COND_VAR()) )
return RC(ENOMEM); // (out of memory)
if ( !InitializeFT_COND_VAR
(
pFT_COND_VAR->hCondVar
))
{
free ( pFT_COND_VAR->hCondVar );
pFT_COND_VAR->dwCondMagic = 0;
pFT_COND_VAR->hCondVar = NULL;
return RC(EAGAIN); // (unable to obtain required resources)
}
pFT_COND_VAR->dwCondMagic = FT_COND_MAGIC;
return RC(0);
}
////////////////////////////////////////////////////////////////////////////////////
// Destroy a "condition"...
DLL_EXPORT
int fthread_cond_destroy
(
fthread_cond_t* pFT_COND_VAR
)
{
if ( !pFT_COND_VAR )
return RC(EINVAL); // (invalid ptr)
if ( FT_COND_MAGIC != pFT_COND_VAR->dwCondMagic )
return RC(EINVAL); // (not initialized)
if ( !UninitializeFT_COND_VAR
(
pFT_COND_VAR->hCondVar
))
return RC(EBUSY); // (still in use)
free ( pFT_COND_VAR->hCondVar );
pFT_COND_VAR->dwCondMagic = 0;
pFT_COND_VAR->hCondVar = NULL;
return RC(0);
}
////////////////////////////////////////////////////////////////////////////////////
// 'Signal' a "condition"... (causes ONE waiting thread to be released)
DLL_EXPORT
int fthread_cond_signal
(
fthread_cond_t* pFT_COND_VAR
)
{
if ( !pFT_COND_VAR )
return RC(EINVAL); // (invalid ptr)
if ( FT_COND_MAGIC != pFT_COND_VAR->dwCondMagic )
return RC(EINVAL); // (not initialized)
return QueueTransmission
(
pFT_COND_VAR->hCondVar,
FALSE // (FALSE == not "broadcast")
);
}
////////////////////////////////////////////////////////////////////////////////////
// 'Broadcast' a "condition"... (causes ALL waiting threads to be released)
DLL_EXPORT
int fthread_cond_broadcast
(
fthread_cond_t* pFT_COND_VAR
)
{
if ( !pFT_COND_VAR )
return RC(EINVAL); // (invalid ptr)
if ( FT_COND_MAGIC != pFT_COND_VAR->dwCondMagic )
return RC(EINVAL); // (not initialized)
return QueueTransmission
(
pFT_COND_VAR->hCondVar,
TRUE // (TRUE == "broadcast" type)
);
}
////////////////////////////////////////////////////////////////////////////////////
// Wait for a "condition" to occur...
DLL_EXPORT
int fthread_cond_wait
(
fthread_cond_t* pFT_COND_VAR,
fthread_mutex_t* pFTUSER_MUTEX
)
{
int rc;
if (0
|| !pFT_COND_VAR // (invalid ptr)
|| FT_COND_MAGIC != pFT_COND_VAR -> dwCondMagic // (not initialized)
|| !pFTUSER_MUTEX // (invalid ptr)
|| FT_MUTEX_MAGIC != pFTUSER_MUTEX -> dwMutexMagic // (not initialized)
)
return RC(EINVAL);
// The following call essentially atomically releases the caller's mutex
// and does a wait. Of course, it doesn't really do the wait though; that's
// actually done further below. BUT, it does atomically register the fact
// that this thread *wishes* to do a wait by acquiring the condition variable
// lock and then incrementing the #of waiters counter before it releases the
// original mutex. Thus, whenever the below function call returns back to us,
// we can be assured that: 1) our request to wait on this condition variable
// has been registered AND 2) we have control of the condition variable in
// question (i.e. we still hold the condition variable lock thus preventing
// anyone from trying to send a signal just yet)...
if
(
(
rc = BeginWait
(
pFT_COND_VAR -> hCondVar,
pFTUSER_MUTEX
)
)
!= 0
)
{
// OOPS! Something went wrong. The original mutex has NOT been released
// and we did NOT acquire our condition variable lock (and thus our wait
// was not registered). Thus we can safely return back to the caller with
// the original mutex still owned (held) by the caller.
return RC(rc); // (return error code to caller; their wait failed)
}
// We only reach here if the condition var was successfully acquired AND our
// wait was registered AND the original mutex was released so the signal can
// be sent (but the signal (transmission) of course cannot ever be sent until
// we first release our lock on our condition variable, which is of course is
// what the below WaitForTransmission function call does within its wait loop)...
rc = WaitForTransmission // (wait for "signal" or "broadcast"...)
(
pFT_COND_VAR->hCondVar,
NULL
);
// A signal (transmission) was sent and we're one of the ones (or the
// only one) that's supposed to receive it...
// If we're the only one that's supposed to receive this transmission,
// then we need to turn off the transmitter (stop "sending" the signal)
// so that no other threads get "woken up" (released) as a result of
// this particular "signal" (transmission)...
// (Note that the below call also de-registers our wait too)
ReceiveXmission // (reset transmitter)
(
pFT_COND_VAR->hCondVar
);
// Release the condition var lock (since we're done with it) and then
// reacquire the caller's original mutex (if possible) and then return
// back to the original caller with their original mutex held with what-
// ever return code got set by the above wait call...
return ReturnFromWait
(
pFT_COND_VAR -> hCondVar,
pFTUSER_MUTEX,
rc
);
}
////////////////////////////////////////////////////////////////////////////////////
// Wait (but not forever) for a "condition" to occur...
// Refer to the comments in the above 'fthread_cond_wait' function (as well as all
// of our other internal functions too of course (BeginWait, WaitForTransmission,
// ReceiveXmission and ReturnFromWait)) for details regarding what's going on here
// (i.e. what we're doing below and why). The below function is essentially identical
// to the above 'fthread_cond_wait' function except that we don't wait forever; we
// only wait for a limited amount of time. Other than that they're exactly identical
// so there's no sense in repeating myself here...
DLL_EXPORT
int fthread_cond_timedwait
(
fthread_cond_t* pFT_COND_VAR,
fthread_mutex_t* pFTUSER_MUTEX,
struct timespec* pTimeTimeout
)
{
int rc;
if (0
|| !pFT_COND_VAR
|| FT_COND_MAGIC != pFT_COND_VAR -> dwCondMagic
|| !pFTUSER_MUTEX
|| FT_MUTEX_MAGIC != pFTUSER_MUTEX -> dwMutexMagic
|| !pTimeTimeout
)
return RC(EINVAL);
if
(
(
rc = BeginWait
(
pFT_COND_VAR -> hCondVar,
pFTUSER_MUTEX
)
)
!= 0
)
return rc;
rc = WaitForTransmission
(
pFT_COND_VAR->hCondVar,
pTimeTimeout
);
ReceiveXmission
(
pFT_COND_VAR->hCondVar
);
return ReturnFromWait
(
pFT_COND_VAR -> hCondVar,
pFTUSER_MUTEX,
rc
);
}
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
// Initialize a "mutex" attribute...
DLL_EXPORT
int fthread_mutexattr_init ( fthread_mutexattr_t* pFT_MUTEX_ATTR )
{
if ( !pFT_MUTEX_ATTR )
return RC(EINVAL);
*pFT_MUTEX_ATTR = FTHREAD_MUTEX_DEFAULT;
return RC(0);
}
////////////////////////////////////////////////////////////////////////////////////
// Destroy a "mutex" attribute...
DLL_EXPORT
int fthread_mutexattr_destroy ( fthread_mutexattr_t* pFT_MUTEX_ATTR )
{
if ( !pFT_MUTEX_ATTR )
return RC(EINVAL);
*pFT_MUTEX_ATTR = 0xCDCDCDCD;
return RC(0);
}
////////////////////////////////////////////////////////////////////////////////////
// Retrieve "mutex" attribute type...
DLL_EXPORT
int fthread_mutexattr_gettype
(
const fthread_mutexattr_t* pFT_MUTEX_ATTR,
int* pnMutexType
)
{
DWORD dwMutexType;
if ( !pFT_MUTEX_ATTR || !pnMutexType || !IsValidMutexType ( dwMutexType = *pFT_MUTEX_ATTR ) )
return RC(EINVAL);
*pnMutexType = (int) dwMutexType;
return RC(0);
}
////////////////////////////////////////////////////////////////////////////////////
// Set "mutex" attribute type...
DLL_EXPORT
int fthread_mutexattr_settype
(
fthread_mutexattr_t* pFT_MUTEX_ATTR,
int nMutexType
)
{
if ( !pFT_MUTEX_ATTR || !IsValidMutexType ( (DWORD) nMutexType ) )
return RC(EINVAL);
*pFT_MUTEX_ATTR = (DWORD) nMutexType;
return RC(0);
}
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
#endif // !defined(OPTION_FTHREADS)
hercules-3.13/hetmap.c 0000664 0001754 0001754 00000022773 13066015061 011640 0000000 0000000 /* HETMAP.C (c) Copyright Leland Lucius, 2000-2009 */
/* Displays information about a Hercules Emulated Tape */
/*
|| ----------------------------------------------------------------------------
||
|| HETMAP.C (c) Copyright Leland Lucius, 2000-2009
|| Released under terms of the Q Public License.
||
|| Displays information about the structure of a Hercules Emulated Tape.
||
|| ----------------------------------------------------------------------------
*/
#include "hstdinc.h"
#include "hercules.h"
#include "hetlib.h"
#include "sllib.h"
#include "herc_getopt.h"
/*
|| Local constant data
*/
static const char sep[] = "---------------------\n";
static const char help_hetmap[] =
"%s - Print a map of an HET or AWS tape file\n\n"
"Usage: %s [options] filename\n\n"
"Options:\n"
" -a print all label and file information (default: on)\n"
" -d print only dataset information (default: off)\n"
" -f print only file information (default: off)\n"
" -h display usage summary\n"
" -l print only label information (default: off)\n"
" -t print TAPEMAP-compatible format output (default: off)\n";
static const char help_tapemap[] =
"%s - Print a map of an HET or AWS tape file\n\n"
"Usage: %s filename\n\n";
#ifdef EXTERNALGUI
/* Previous reported file position */
static off_t prevpos = 0;
/* Report progress every this many bytes */
#define PROGRESS_MASK (~0x3FFFF /* 256K */)
#endif /*EXTERNALGUI*/
/*
|| Print terse dataset information (from VOL1/EOF1/EOF2)
*/
void
printdataset( char *buf, int len, int fileno )
{
SLLABEL lab;
SLFMT fmt;
char crtdt[ 9 ];
char expdt[ 9 ];
char recfm[ 4 ];
if( sl_islabel( &lab, buf, len ) == FALSE )
{
return;
}
sl_fmtlab( &fmt, &lab );
if( sl_isvol( buf, 1 ) )
{
printf( "vol=%-17.17s owner=%s\n\n",
fmt.slvol.volser,
fmt.slvol.owner);
}
else if( sl_iseof( buf, 1 ) )
{
printf( "seq=%-17d file#=%d\n",
atoi( fmt.slds1.dsseq ),
fileno );
printf( "dsn=%-17.17s crtdt=%-8.8s expdt=%-8.8s blocks=%d\n",
fmt.slds1.dsid,
sl_fmtdate( crtdt, fmt.slds1.crtdt, TRUE ),
sl_fmtdate( expdt, fmt.slds1.expdt, TRUE ),
atoi( fmt.slds1.blkhi ) * 1000000 + atoi( fmt.slds1.blklo ) );
}
else if( sl_iseof( buf, 2 ) )
{
recfm[ 0 ] = '\0';
strcat( strcat( strcat( recfm,
fmt.slds2.recfm ),
fmt.slds2.blkattr ),
fmt.slds2.ctrl );
printf( "job=%17.17s recfm=%-3.3s lrecl=%-5d blksize=%-5d\n\n",
fmt.slds2.jobid,
recfm,
atoi( fmt.slds2.lrecl ),
atoi( fmt.slds2.blksize ) );
}
return;
}
/*
|| Print all label fields
*/
void
printlabel( char *buf, int len )
{
SLLABEL lab;
SLFMT fmt;
int i;
if( sl_islabel( &lab, buf, len ) == FALSE )
{
return;
}
sl_fmtlab( &fmt, &lab );
printf( sep );
for( i = 0; fmt.key[ i ] != NULL; i++ )
{
printf("%-20.20s: '%s'\n", fmt.key[ i ] , fmt.val[ i ] );
}
return;
}
/*
|| Print label fields in TAPEMAP format
*/
void
printlabeltapemap( char *buf, int len )
{
SLLABEL lab;
BYTE labelrec[81];
int i;
if( sl_islabel( &lab, buf, len ) == FALSE )
{
return;
}
for (i=0; i < 80; i++)
{
labelrec[i] = guest_to_host(buf[i]);
}
labelrec[i] = '\0';
printf("%s\n", labelrec);
}
/*
|| Prints usage information
*/
void
usage( char *name )
{
if ((strcmp(name, "tapemap") == 0) || (strcmp(name, "TAPEMAP") == 0))
{
printf( help_tapemap, name, name );
}
else
{
printf( help_hetmap, name, name );
}
}
/*
|| Standard main
*/
int
main( int argc, char *argv[] )
{
char buf[ HETMAX_BLOCKSIZE ];
HETB *hetb;
int rc;
int fileno;
U32 blocks;
U32 uminsz;
U32 umaxsz;
U32 ubytes;
U32 cminsz;
U32 cmaxsz;
U32 cbytes;
U32 totblocks;
U32 totubytes;
U32 totcbytes;
U32 opts = 0;
char pgmpath[MAX_PATH];
char *pgm;
#define O_ALL 0xC0
#define O_FILES 0X80
#define O_LABELS 0X40
#define O_DATASETS 0X20
#define O_TAPEMAP_OUTPUT 0x10
#define O_TAPEMAP_INVOKED 0x08
/* Figure out processing based on the program name */
hostpath(pgmpath, argv[0], sizeof(pgmpath));
pgm = strrchr(pgmpath, '/');
if (pgm)
{
pgm++;
}
else
{
pgm = argv[0];
}
strtok (pgm, ".");
if ((strcmp(pgm, "tapemap") == 0) || (strcmp(pgm, "TAPEMAP") == 0))
{
opts = O_TAPEMAP_OUTPUT+O_TAPEMAP_INVOKED;
}
INITIALIZE_UTILITY(pgm);
/* Display the program identification message */
display_version (stderr, "Hercules HET and AWS tape map program ", FALSE);
if (! (opts & O_TAPEMAP_INVOKED) )
{
opts = O_ALL;
while( TRUE )
{
rc = getopt( argc, argv, "adfhlt" );
if( rc == -1 )
{
break;
}
switch( rc )
{
case 'a':
opts = O_ALL;
break;
case 'd':
opts = O_DATASETS;
break;
case 'f':
opts = O_FILES;
break;
case 'h':
usage( pgm );
exit( 1 );
break;
case 'l':
opts = O_LABELS;
break;
case 't':
opts = O_TAPEMAP_OUTPUT;
break;
default:
usage( pgm );
exit( 1 );
break;
}
}
} // end if (! (opts & O_TAPEMAP_INVOKED) )
argc -= optind;
if( argc != 1 )
{
usage( pgm );
exit( 1 );
}
if( opts & O_ALL )
{
printf( sep );
printf( "%-20.20s: %s\n", "Filename", argv[ optind ] );
}
rc = het_open( &hetb, argv[ optind ], 0 );
if( rc < 0 )
{
printf( "het_open() returned %d\n", rc );
het_close( &hetb );
exit( 1 );
}
fileno = 0;
blocks = 0;
uminsz = 0;
umaxsz = 0;
ubytes = 0;
cminsz = 0;
cmaxsz = 0;
cbytes = 0;
totblocks = 0;
totubytes = 0;
totcbytes = 0;
while( TRUE )
{
#ifdef EXTERNALGUI
if( extgui )
{
/* Report progress every nnnK */
off_t curpos = ftell( hetb->fd );
if( ( curpos & PROGRESS_MASK ) != ( prevpos & PROGRESS_MASK ) )
{
prevpos = curpos;
fprintf( stderr, "IPOS=%" I64_FMT "d\n", (U64)curpos );
}
}
#endif /*EXTERNALGUI*/
rc = het_read( hetb, buf );
if( rc == HETE_EOT )
{
if( opts & O_TAPEMAP_OUTPUT )
{
printf ("End of tape.\n");
}
break;
}
if( rc == HETE_TAPEMARK )
{
fileno += 1;
if( opts & O_TAPEMAP_OUTPUT )
{
printf ("File %u: Blocks=%u, block size min=%u, max=%u\n",
fileno, blocks, uminsz, umaxsz );
}
if( opts & O_FILES )
{
printf( sep );
printf( "%-20.20s: %d\n", "File #", fileno );
printf( "%-20.20s: %d\n", "Blocks", blocks );
printf( "%-20.20s: %d\n", "Min Blocksize", uminsz );
printf( "%-20.20s: %d\n", "Max Blocksize", umaxsz );
printf( "%-20.20s: %d\n", "Uncompressed bytes", ubytes );
printf( "%-20.20s: %d\n", "Min Blocksize-Comp", cminsz );
printf( "%-20.20s: %d\n", "Max Blocksize-Comp", cmaxsz );
printf( "%-20.20s: %d\n", "Compressed bytes", cbytes );
}
totblocks += blocks;
totubytes += ubytes;
totcbytes += cbytes;
blocks = 0;
uminsz = 0;
umaxsz = 0;
ubytes = 0;
cminsz = 0;
cmaxsz = 0;
cbytes = 0;
continue;
}
if( rc < 0 )
{
printf( "het_read() returned %d\n", rc );
break;
}
blocks += 1;
ubytes += hetb->ublksize;
cbytes += hetb->cblksize;
if( uminsz == 0 || hetb->ublksize < uminsz ) uminsz = hetb->ublksize;
if( hetb->ublksize > umaxsz ) umaxsz = hetb->ublksize;
if( cminsz == 0 || hetb->cblksize < cminsz ) cminsz = hetb->cblksize;
if( hetb->cblksize > cmaxsz ) cmaxsz = hetb->cblksize;
if( opts & O_LABELS )
{
printlabel( buf, rc );
}
if( opts & O_TAPEMAP_OUTPUT )
{
printlabeltapemap( buf, rc );
}
if( opts & O_DATASETS )
{
printdataset( buf, rc, fileno );
}
}
if( opts & O_FILES )
{
printf( sep );
printf( "%-20.20s:\n", "Summary" );
printf( "%-20.20s: %d\n", "Files", fileno );
printf( "%-20.20s: %d\n", "Blocks", totblocks );
printf( "%-20.20s: %d\n", "Uncompressed bytes", totubytes );
printf( "%-20.20s: %d\n", "Compressed bytes", totcbytes );
printf( "%-20.20s: %d\n", "Reduction", totubytes - totcbytes );
}
het_close( &hetb );
return 0;
}
hercules-3.13/dasdinit.c 0000664 0001754 0001754 00000025152 13066015061 012153 0000000 0000000 /* DASDINIT.C (c) Copyright Roger Bowler, 1999-2009 */
/* Hercules DASD Utilities: DASD image builder */
/*-------------------------------------------------------------------*/
/* This program creates a disk image file and initializes it as */
/* a blank FBA or CKD DASD volume. */
/* */
/* The program is invoked from the shell prompt using the command: */
/* */
/* dasdinit [-options] filename devtype[-model] [volser] [size] */
/* */
/* options options: */
/* -a include alternate cylinders */
/* (ignored if size specified manually) */
/* -z build compressed device using zlib */
/* -bz2 build compressed device using bzip2 */
/* -0 build compressed device with no compression */
/* -r "raw" init (bypass VOL1 & IPL track fmt) */
/* */
/* filename is the name of the disk image file to be created */
/* (this program will not overwrite an existing file) */
/* */
/* devtype is the emulated device type. */
/* CKD: 2305, 2311, 2314, 3330, 3350, 3375, 3380, */
/* 3390, 9345 */
/* FBA: 0671, 3310, 3370, 9313, 9332, 9335, 9336 */
/* */
/* model is the device model number and implies the device */
/* size. If specified, then size shouldn't be specified.*/
/* */
/* volser is the volume serial number (1-6 characters) */
/* (only if '-r' option not used) */
/* */
/* size is the size of the device (in cylinders for CKD */
/* devices, or in 512-byte sectors for FBA devices). */
/* Shouldn't be specified if model is specified. */
/* */
/*-------------------------------------------------------------------*/
#include "hstdinc.h"
#include "hercules.h"
#include "dasdblks.h"
/*-------------------------------------------------------------------*/
/* Subroutine to display command syntax and exit */
/*-------------------------------------------------------------------*/
static void
argexit ( int code, char *m )
{
switch (code) {
case 0:
fprintf (stderr, "Invalid or unsupported option: %s\n",
m ? m : "(null)");
break;
case 1:
fprintf (stderr, "Invalid or missing filename: %s\n",
m ? m : "(null)");
break;
case 2:
fprintf (stderr, "Invalid or missing device type: %s\n",
m ? m : "(null)");
break;
case 3:
fprintf (stderr, "Invalid or missing volser: %s\n",
m ? m : "(null)");
break;
case 4:
fprintf (stderr, "Invalid or missing size: %s\n",
m ? m : "(null)");
break;
case 5:
fprintf (stderr, "Invalid number of arguments\n");
break;
case 6:
fprintf (stderr, "`-linux' only supported for device type 3390\n");
break;
default:
display_version (stderr,
"Hercules DASD image file creation program\n", FALSE);
fprintf (stderr,
"Builds an empty dasd image file:\n\n"
" dasdinit [-options] filename devtype[-model] [volser] [size]\n\n"
"where:\n\n"
" -v display version info and help\n"
#ifdef HAVE_LIBZ
" -z build compressed dasd image file using zlib\n"
#endif
#ifdef CCKD_BZIP2
" -bz2 build compressed dasd image file using bzip2\n"
#endif
" -0 build compressed dasd image file with no compression\n"
);
if (sizeof(off_t) > 4) fprintf(stderr,
" -lfs build a large (uncompressed) dasd file (if supported)\n"
);
fprintf(stderr,
" -a build dasd image file that includes alternate cylinders\n"
" (option ignored if size is manually specified)\n"
" -r build 'raw' dasd image file (no VOL1 or IPL track)\n"
" -linux null track images will look like linux dasdfmt'ed images\n"
" (3390 device type only)\n\n"
" filename name of dasd image file to be created\n\n"
" devtype CKD: 2305, 2311, 2314, 3330, 3340, 3350, 3375, 3380, 3390, 9345\n"
" FBA: 0671, 3310, 3370, 9313, 9332, 9335, 9336\n\n"
" model device model (implies size) (opt)\n\n"
" volser volume serial number (1-6 characters)\n"
" (specified only if '-r' option not used)\n\n"
" size number of CKD cylinders or 512-byte FBA sectors\n"
" (required if model not specified else optional)\n"
);
break;
}
exit(code);
} /* end function argexit */
/*-------------------------------------------------------------------*/
/* DASDINIT program main entry point */
/*-------------------------------------------------------------------*/
int main ( int argc, char *argv[] )
{
int altcylflag = 0; /* Alternate cylinders flag */
int rawflag = 0; /* Raw format flag */
int volsize_argnum = 4; /* argc value of size option */
U32 size = 0; /* Volume size */
U32 altsize = 0; /* Alternate cylinders */
U32 heads = 0; /* Number of tracks/cylinder */
U32 maxdlen = 0; /* Maximum R1 data length */
U32 sectsize = 0; /* Sector size */
U16 devtype = 0; /* Device type */
BYTE comp = 0xff; /* Compression algoritm */
BYTE type = 0; /* C=CKD, F=FBA */
char fname[1024]; /* File name */
char volser[7]; /* Volume serial number */
BYTE c; /* Character work area */
CKDDEV *ckd; /* -> CKD device table entry */
FBADEV *fba; /* -> FBA device table entry */
int lfs = 0; /* 1 = Build large file */
int nullfmt = CKDDASD_NULLTRK_FMT1; /* Null track format type */
int rc; /* Return code */
INITIALIZE_UTILITY("dasdinit");
/* Display program identification and help */
if (argc <= 1 || (argc == 2 && !strcmp(argv[1], "-v")))
argexit(-1, NULL);
/* Process optional arguments */
for ( ; argc > 1 && argv[1][0] == '-'; argv++, argc--)
{
if (strcmp("0", &argv[1][1]) == 0)
comp = CCKD_COMPRESS_NONE;
#ifdef HAVE_LIBZ
else if (strcmp("z", &argv[1][1]) == 0)
comp = CCKD_COMPRESS_ZLIB;
#endif
#ifdef CCKD_BZIP2
else if (strcmp("bz2", &argv[1][1]) == 0)
comp = CCKD_COMPRESS_BZIP2;
#endif
else if (strcmp("a", &argv[1][1]) == 0)
altcylflag = 1;
else if (strcmp("r", &argv[1][1]) == 0)
rawflag = 1;
else if (strcmp("lfs", &argv[1][1]) == 0 && sizeof(off_t) > 4)
lfs = 1;
else if (strcmp("linux", &argv[1][1]) == 0)
nullfmt = CKDDASD_NULLTRK_FMT2;
else argexit(0, argv[1]);
}
/* Check remaining number of arguments */
if (argc < (rawflag ? 3 : 4) || argc > (rawflag ? 4 : 5))
argexit(5, NULL);
/* The first argument is the file name */
if (argv[1] == NULL || strlen(argv[1]) == 0
|| strlen(argv[1]) > sizeof(fname)-1)
argexit(1, argv[1]);
strcpy (fname, argv[1]);
/* The second argument is the device type.
Model number may also be specified */
if (argv[2] == NULL)
argexit(2, argv[2]);
ckd = dasd_lookup (DASD_CKDDEV, argv[2], 0, 0);
if (ckd != NULL)
{
type = 'C';
devtype = ckd->devt;
size = ckd->cyls;
altsize = ckd->altcyls;
heads = ckd->heads;
maxdlen = ckd->r1;
}
else
{
fba = dasd_lookup (DASD_FBADEV, argv[2], 0, 0);
if (fba != NULL)
{
type = 'F';
devtype = fba->devt;
size = fba->blks;
altsize = 0;
sectsize = fba->size;
}
}
if (!type)
/* Specified model not found */
argexit(2, argv[2]);
/* If -r option specified, then there is not volume serial
argument and volume size argument is actually argument
number 3 and not argument number 4 as otherwise */
if (rawflag)
volsize_argnum = 3;
else
{
volsize_argnum = 4;
/* The third argument is the volume serial number */
if (argv[3] == NULL || strlen(argv[3]) == 0
|| strlen(argv[3]) > sizeof(volser)-1)
argexit(3, argv[3]);
strcpy (volser, argv[3]);
string_to_upper (volser);
}
/* The fourth argument (or third for -r) is the volume size */
if (argc > volsize_argnum)
{
if (argc > (volsize_argnum+1))
argexit(5, NULL);
if (!argv[volsize_argnum] || strlen(argv[volsize_argnum]) == 0
|| sscanf(argv[volsize_argnum], "%u%c", &size, &c) != 1)
argexit(4, argv[volsize_argnum]);
altcylflag = 0;
}
/* `-linux' only supported for 3390 device type */
if (nullfmt == CKDDASD_NULLTRK_FMT2 && devtype != 0x3390)
argexit(6, NULL);
if (altcylflag)
size += altsize;
/* Create the device */
if (type == 'C')
rc = create_ckd (fname, devtype, heads, maxdlen, size, volser,
comp, lfs, 0, nullfmt, rawflag);
else
rc = create_fba (fname, devtype, sectsize, size, volser, comp,
lfs, 0, rawflag);
/* Display completion message */
if (rc == 0)
{
fprintf (stderr, _("HHCDI001I DASD initialization successfully "
"completed.\n"));
} else {
fprintf (stderr, _("HHCDI002I DASD initialization unsuccessful"
"\n"));
}
return rc;
} /* end function main */
hercules-3.13/bldcfg.c 0000664 0001754 0001754 00000200113 13066015061 011565 0000000 0000000 /* BLDCFG.C (c) Copyright Roger Bowler, 1999-2009 */
/* ESA/390 Configuration Builder */
/* Interpretive Execution - (c) Copyright Jan Jaeger, 1999-2009 */
/*-------------------------------------------------------------------*/
/* This module builds the configuration tables for the Hercules */
/* ESA/390 emulator. It reads information about the processors */
/* and I/O devices from a configuration file. It allocates */
/* main storage and expanded storage, initializes control blocks, */
/* and creates detached threads to handle console attention */
/* requests and to maintain the TOD clock and CPU timers. */
/*-------------------------------------------------------------------*/
/*-------------------------------------------------------------------*/
/* Additional credits: */
/* TOD clock offset contributed by Jay Maynard */
/* Dynamic device attach/detach by Jan Jaeger */
/* OSTAILOR parameter by Jay Maynard */
/* PANRATE parameter by Reed H. Petty */
/* CPUPRIO parameter by Jan Jaeger */
/* HERCPRIO, TODPRIO, DEVPRIO parameters by Mark L. Gaubatz */
/* z/Architecture support - (c) Copyright Jan Jaeger, 1999-2009 */
/* $(DEFSYM) symbol substitution support by Ivan Warren */
/* Patch for ${var=def} symbol substitution (hax #26), */
/* and INCLUDE support (modified hax #27), */
/* contributed by Enrico Sorichetti based on */
/* original patches by "Hackules" */
/*-------------------------------------------------------------------*/
#include "hstdinc.h"
#if !defined(_BLDCFG_C_)
#define _BLDCFG_C_
#endif
#if !defined(_HENGINE_DLL_)
#define _HENGINE_DLL_
#endif
#include "hercules.h"
#include "devtype.h"
#include "opcode.h"
#include "hostinfo.h"
#if defined(OPTION_FISHIO)
#include "w32chan.h"
#endif // defined(OPTION_FISHIO)
#if defined( OPTION_TAPE_AUTOMOUNT )
#include "tapedev.h"
#endif
#if !defined(_GEN_ARCH)
#if defined(_ARCHMODE3)
#define _GEN_ARCH _ARCHMODE3
#include "bldcfg.c"
#undef _GEN_ARCH
#endif
#if defined(_ARCHMODE2)
#define _GEN_ARCH _ARCHMODE2
#include "bldcfg.c"
#undef _GEN_ARCH
#endif
typedef struct _DEVARRAY
{
U16 cuu1;
U16 cuu2;
} DEVARRAY;
typedef struct _DEVNUMSDESC
{
BYTE lcss;
DEVARRAY *da;
} DEVNUMSDESC;
/*-------------------------------------------------------------------*/
/* Static data areas */
/*-------------------------------------------------------------------*/
#define MAX_INC_LEVEL 8 /* Maximum nest level */
static int inc_level; /* Current nesting level */
// following commented out ISW 20061009 : Not referenced anywhere.
// static int inc_fname[MAX_INC_LEVEL]; /* filename (base or incl) */
static int inc_stmtnum[MAX_INC_LEVEL]; /* statement number */
static int inc_ignore_errors = 0; /* 1==ignore include errors */
#ifdef EXTERNALGUI
static char buf[1024]; /* Config statement buffer */
#else /*!EXTERNALGUI*/
static char buf[256]; /* Config statement buffer */
#endif /*EXTERNALGUI*/
static char *keyword; /* -> Statement keyword */
static char *operand; /* -> First argument */
static int addargc; /* Number of additional args */
static char *addargv[MAX_ARGS]; /* Additional argument array */
/*-------------------------------------------------------------------*/
/* Subroutine to parse an argument string. The string that is passed */
/* is modified in-place by inserting null characters at the end of */
/* each argument found. The returned array of argument pointers */
/* then points to each argument found in the original string. Any */
/* argument that begins with '#' comment indicator causes early */
/* termination of the parsing and is not included in the count. Any */
/* argument found that starts with a quote or apostrophe causes */
/* all characters up to the next quote or apostrophe to be */
/* included as part of that argument. The quotes/apostrophes them- */
/* selves are not considered part of any argument and are ignored. */
/* p Points to string to be parsed. */
/* maxargc Maximum allowable number of arguments. (Prevents */
/* overflowing the pargv array) */
/* pargv Pointer to buffer for argument pointer array. */
/* pargc Pointer to number of arguments integer result. */
/* Returns number of arguments found. (same value as at *pargc) */
/*-------------------------------------------------------------------*/
DLL_EXPORT int parse_args (char* p, int maxargc, char** pargv, int* pargc)
{
for (*pargc = 0; *pargc < MAX_ARGS; ++*pargc) addargv[*pargc] = NULL;
*pargc = 0;
*pargv = NULL;
while (*p && *pargc < maxargc)
{
while (*p && isspace(*p)) p++; if (!*p) break; // find start of arg
if (*p == '#') break; // stop on comments
*pargv = p; ++*pargc; // count new arg
while (*p && !isspace(*p) && *p != '\"' && *p != '\'') p++; if (!*p) break; // find end of arg
if (*p == '\"' || *p == '\'')
{
char delim = *p;
if (p == *pargv) *pargv = p+1;
while (*++p && *p != delim) {}; if (!*p) break; // find end of quoted string
}
*p++ = 0; // mark end of arg
pargv++; // next arg ptr
}
return *pargc;
}
void delayed_exit (int exit_code)
{
/* Delay exiting is to give the system
* time to display the error message. */
fflush(stderr);
fflush(stdout);
usleep(100000);
// hdl_shut();
do_shutdown();
fflush(stderr);
fflush(stdout);
usleep(100000);
exit(exit_code);
}
/* storage configuration routine. To be moved *JJ */
static void config_storage(unsigned mainsize, unsigned xpndsize)
{
int off;
/* Obtain main storage */
sysblk.mainsize = mainsize * 1024 * 1024ULL;
sysblk.mainstor = calloc((size_t)(sysblk.mainsize + 8192), 1);
if (sysblk.mainstor != NULL)
sysblk.main_clear = 1;
else
sysblk.mainstor = malloc((size_t)(sysblk.mainsize + 8192));
if (sysblk.mainstor == NULL)
{
logmsg(_("HHCCF031S Cannot obtain %dMB main storage: %s\n"),
mainsize, strerror(errno));
delayed_exit(1);
}
/* Trying to get mainstor aligned to the next 4K boundary - Greg */
off = (uintptr_t)sysblk.mainstor & 0xFFF;
sysblk.mainstor += off ? 4096 - off : 0;
/* Obtain main storage key array */
sysblk.storkeys = calloc((size_t)(sysblk.mainsize / STORAGE_KEY_UNITSIZE), 1);
if (sysblk.storkeys == NULL)
{
sysblk.main_clear = 0;
sysblk.storkeys = malloc((size_t)(sysblk.mainsize / STORAGE_KEY_UNITSIZE));
}
if (sysblk.storkeys == NULL)
{
logmsg(_("HHCCF032S Cannot obtain storage key array: %s\n"),
strerror(errno));
delayed_exit(1);
}
/* Initial power-on reset for main storage */
storage_clear();
#if 0 /*DEBUG-JJ-20/03/2000*/
/* Mark selected frames invalid for debugging purposes */
for (i = 64 ; i < (sysblk.mainsize / STORAGE_KEY_UNITSIZE); i += 2)
if (i < (sysblk.mainsize / STORAGE_KEY_UNITSIZE) - 64)
sysblk.storkeys[i] = STORKEY_BADFRM;
else
sysblk.storkeys[i++] = STORKEY_BADFRM;
#endif
if (xpndsize != 0)
{
#ifdef _FEATURE_EXPANDED_STORAGE
/* Obtain expanded storage */
sysblk.xpndsize = xpndsize * (1024*1024 / XSTORE_PAGESIZE);
sysblk.xpndstor = calloc(sysblk.xpndsize, XSTORE_PAGESIZE);
if (sysblk.xpndstor)
sysblk.xpnd_clear = 1;
else
sysblk.xpndstor = malloc((size_t)sysblk.xpndsize * XSTORE_PAGESIZE);
if (sysblk.xpndstor == NULL)
{
logmsg(_("HHCCF033S Cannot obtain %dMB expanded storage: "
"%s\n"),
xpndsize, strerror(errno));
delayed_exit(1);
}
/* Initial power-on reset for expanded storage */
xstorage_clear();
#else /*!_FEATURE_EXPANDED_STORAGE*/
logmsg(_("HHCCF034W Expanded storage support not installed\n"));
#endif /*!_FEATURE_EXPANDED_STORAGE*/
} /* end if(sysblk.xpndsize) */
}
#if defined( OPTION_TAPE_AUTOMOUNT )
/*-------------------------------------------------------------------*/
/* Add directory to AUTOMOUNT allowed/disallowed directories list */
/* */
/* Input: tamdir pointer to work character array of at least */
/* MAX_PATH size containing an allowed/disallowed */
/* directory specification, optionally prefixed */
/* with the '+' or '-' indicator. */
/* */
/* ppTAMDIR address of TAMDIR ptr that upon successful */
/* completion is updated to point to the TAMDIR */
/* entry that was just successfully added. */
/* */
/* Output: upon success, ppTAMDIR is updated to point to the TAMDIR */
/* entry just added. Upon error, ppTAMDIR is set to NULL and */
/* the original input character array is set to the inter- */
/* mediate value being processed when the error occurred. */
/* */
/* Returns: 0 == success */
/* 1 == unresolvable path */
/* 2 == path inaccessible */
/* 3 == conflict w/previous */
/* 4 == duplicates previous */
/* 5 == out of memory */
/* */
/*-------------------------------------------------------------------*/
DLL_EXPORT int add_tamdir( char *tamdir, TAMDIR **ppTAMDIR )
{
int rc, rej = 0;
char dirwrk[ MAX_PATH ] = {0};
*ppTAMDIR = NULL;
if (*tamdir == '-')
{
rej = 1;
memmove (tamdir, tamdir+1, MAX_PATH);
}
else if (*tamdir == '+')
{
rej = 0;
memmove (tamdir, tamdir+1, MAX_PATH);
}
/* Convert tamdir to absolute path ending with a slash */
#if defined(_MSVC_)
/* (expand any embedded %var% environment variables) */
rc = expand_environ_vars( tamdir, dirwrk, MAX_PATH );
if (rc == 0)
strlcpy (tamdir, dirwrk, MAX_PATH);
#endif
if (!realpath( tamdir, dirwrk ))
return (1); /* ("unresolvable path") */
strlcpy (tamdir, dirwrk, MAX_PATH);
/* Verify that the path is valid */
if (access( tamdir, R_OK | W_OK ) != 0)
return (2); /* ("path inaccessible") */
/* Append trailing path separator if needed */
rc = strlen( tamdir );
if (tamdir[rc-1] != *PATH_SEP)
strlcat (tamdir, PATH_SEP, MAX_PATH);
/* Check for duplicate/conflicting specification */
for (*ppTAMDIR = sysblk.tamdir;
*ppTAMDIR;
*ppTAMDIR = (*ppTAMDIR)->next)
{
if (strfilenamecmp( tamdir, (*ppTAMDIR)->dir ) == 0)
{
if ((*ppTAMDIR)->rej != rej)
return (3); /* ("conflict w/previous") */
else
return (4); /* ("duplicates previous") */
}
}
/* Allocate new AUTOMOUNT directory entry */
*ppTAMDIR = malloc( sizeof(TAMDIR) );
if (!*ppTAMDIR)
return (5); /* ("out of memory") */
/* Fill in the new entry... */
(*ppTAMDIR)->dir = strdup (tamdir);
(*ppTAMDIR)->len = strlen (tamdir);
(*ppTAMDIR)->rej = rej;
(*ppTAMDIR)->next = NULL;
/* Add new entry to end of existing list... */
if (sysblk.tamdir == NULL)
sysblk.tamdir = *ppTAMDIR;
else
{
TAMDIR *pTAMDIR = sysblk.tamdir;
while (pTAMDIR->next)
pTAMDIR = pTAMDIR->next;
pTAMDIR->next = *ppTAMDIR;
}
/* Use first allowable dir as default */
if (rej == 0 && sysblk.defdir == NULL)
sysblk.defdir = (*ppTAMDIR)->dir;
return (0); /* ("success") */
}
#endif /* OPTION_TAPE_AUTOMOUNT */
/*-------------------------------------------------------------------*/
/* Subroutine to read a statement from the configuration file */
/* The statement is then parsed into keyword, operand, and */
/* additional arguments. The output values are: */
/* keyword Points to first word of statement */
/* operand Points to second word of statement */
/* addargc Contains number of additional arguments */
/* addargv An array of pointers to each additional argument */
/* Returns 0 if successful, -1 if end of file */
/*-------------------------------------------------------------------*/
static int read_config (char *fname, FILE *fp)
{
int i; /* Array subscript */
int c; /* Character work area */
int stmtlen; /* Statement length */
#if defined( OPTION_ENHANCED_CONFIG_SYMBOLS )
int inc_dollar; /* >=0 Ndx of dollar */
int inc_lbrace; /* >=0 Ndx of lbrace + 1 */
int inc_colon; /* >=0 Ndx of colon */
int inc_equals; /* >=0 Ndx of equals */
char *inc_envvar; /* ->Environment variable */
#endif // defined( OPTION_ENHANCED_CONFIG_SYMBOLS )
int lstarted; /* Indicate if non-whitespace*/
/* has been seen yet in line */
char *cnfline; /* Pointer to copy of buffer */
#if defined(OPTION_CONFIG_SYMBOLS)
char *buf1; /* Pointer to resolved buffer*/
#endif /*defined(OPTION_CONFIG_SYMBOLS)*/
#if defined( OPTION_ENHANCED_CONFIG_SYMBOLS )
inc_dollar = -1;
inc_lbrace = -1;
inc_colon = -1;
inc_equals = -1;
#endif // defined( OPTION_ENHANCED_CONFIG_SYMBOLS )
while (1)
{
/* Increment statement number */
inc_stmtnum[inc_level]++;
/* Read next statement from configuration file */
for (stmtlen = 0, lstarted = 0; ;)
{
/* Read character from configuration file */
c = fgetc(fp);
/* Check for I/O error */
if (ferror(fp))
{
logmsg(_("HHCCF001S Error reading file %s line %d: %s\n"),
fname, inc_stmtnum[inc_level], strerror(errno));
delayed_exit(1);
}
/* Check for end of file */
if (stmtlen == 0 && (c == EOF || c == '\x1A'))
return -1;
/* Check for end of line */
if (c == '\n' || c == EOF || c == '\x1A')
break;
/* Ignore nulls and carriage returns */
if (c == '\0' || c == '\r') continue;
/* Check if it is a white space and no other character yet */
if(!lstarted && isspace(c)) continue;
lstarted=1;
/* Check that statement does not overflow buffer */
if (stmtlen >= (int)(sizeof(buf) - 1))
{
logmsg(_("HHCCF002S File %s line %d is too long\n"),
fname, inc_stmtnum[inc_level]);
delayed_exit(1);
}
#if defined( OPTION_ENHANCED_CONFIG_SYMBOLS )
/* inc_dollar already processed? */
if (inc_dollar >= 0)
{
/* Left brace already processed? */
if (inc_lbrace >= 0)
{
/* End of variable spec? */
if (c == '}')
{
/* Terminate it */
buf[stmtlen] = '\0';
/* Terminate var name if we have a inc_colon specifier */
if (inc_colon >= 0)
{
buf[inc_colon] = '\0';
}
/* Terminate var name if we have a default value */
if (inc_equals >= 0)
{
buf[inc_equals] = '\0';
}
/* Reset statement index to start of variable */
stmtlen = inc_dollar;
/* Get variable value */
inc_envvar = getenv (&buf[inc_lbrace]);
/* Variable unset? */
if (inc_envvar == NULL)
{
/* Substitute default if specified */
if (inc_equals >= 0)
{
inc_envvar = &buf[inc_equals+1];
}
}
else // (environ variable defined)
{
/* Have ":=" specification? */
if (/*inc_colon >= 0 && */inc_equals >= 0)
{
/* Substitute default if value is NULL */
if (strlen (inc_envvar) == 0)
{
inc_envvar = &buf[inc_equals+1];
}
}
}
/* Have a value? (environment or default) */
if (inc_envvar != NULL)
{
/* Check that statement does not overflow buffer */
if (stmtlen+strlen(inc_envvar) >= sizeof(buf) - 1)
{
logmsg(_("HHCCF002S File %s line %d is too long\n"),
fname, inc_stmtnum[inc_level]);
delayed_exit(1);
}
/* Copy to buffer and update index */
stmtlen += sprintf (&buf[stmtlen], "%s", inc_envvar);
}
/* Reset indexes */
inc_equals = -1;
inc_colon = -1;
inc_lbrace = -1;
inc_dollar = -1;
continue;
}
else if (c == ':' && inc_colon < 0 && inc_equals < 0)
{
/* Remember possible start of default specifier */
inc_colon = stmtlen;
}
else if (c == '=' && inc_equals < 0)
{
/* Remember possible start of default specifier */
inc_equals = stmtlen;
}
}
else // (inc_lbrace < 0)
{
/* Remember start of variable name */
if (c == '{')
{
inc_lbrace = stmtlen + 1;
}
else
{
/* Reset inc_dollar specifier if immediately following
character is not a left brace */
inc_dollar = -1;
}
}
}
else // (inc_dollar < 0)
{
/* Enter variable substitution state */
if (c == '$')
{
inc_dollar = stmtlen;
}
}
#endif // defined( OPTION_ENHANCED_CONFIG_SYMBOLS )
/* Append character to buffer */
buf[stmtlen++] = c;
} /* end for(stmtlen) */
/* Remove trailing blanks and tabs */
while (stmtlen > 0 && (buf[stmtlen-1] == SPACE
|| buf[stmtlen-1] == '\t')) stmtlen--;
buf[stmtlen] = '\0';
/* Ignore comments and null statements */
if (stmtlen == 0 || buf[0] == '*' || buf[0] == '#')
continue;
cnfline = strdup(buf);
/* Parse the statement just read */
#if defined(OPTION_CONFIG_SYMBOLS)
/* Perform variable substitution */
/* First, set some 'dynamic' symbols to their own values */
set_symbol("CUU","$(CUU)");
set_symbol("cuu","$(cuu)");
set_symbol("CCUU","$(CCUU)");
set_symbol("ccuu","$(ccuu)");
/* VERISION will be set here, earlier than in @PJJ */
/* console.c in order to make it usable in the @PJJ */
/* hercules configuration file. @PJJ */
set_symbol("VERSION", VERSION);
buf1=resolve_symbol_string(buf);
if(buf1!=NULL)
{
if(strlen(buf1)>=sizeof(buf))
{
logmsg(_("HHCCF002S File %s line %d is too long\n"),
fname, inc_stmtnum[inc_level]);
free(buf1);
delayed_exit(1);
}
strcpy(buf,buf1);
/* Free buf1 as explicitly stated to be needed in @PJJ */
/* resolve_symbol_string. @PJJ */
free(buf1);
}
#endif /*defined(OPTION_CONFIG_SYMBOLS)*/
parse_args (buf, MAX_ARGS, addargv, &addargc);
#if defined(OPTION_DYNAMIC_LOAD)
if(config_command)
{
if( config_command(addargc, (char**)addargv, cnfline) )
{
free(cnfline);
continue;
}
}
#endif /*defined(OPTION_DYNAMIC_LOAD)*/
if( !ProcessConfigCommand (addargc, (char**)addargv, cnfline) )
{
free(cnfline);
continue;
}
free(cnfline);
/* Move the first two arguments to separate variables */
keyword = addargv[0];
operand = addargv[1];
addargc = (addargc > 2) ? (addargc-2) : (0);
for (i = 0; i < MAX_ARGS; i++)
{
if (i < (MAX_ARGS-2)) addargv[i] = addargv[i+2];
else addargv[i] = NULL;
}
break;
} /* end while */
return 0;
} /* end function read_config */
static inline S64 lyear_adjust(int epoch)
{
int year, leapyear;
U64 tod = hw_clock();
if(tod >= TOD_YEAR)
{
tod -= TOD_YEAR;
year = (tod / TOD_4YEARS * 4) + 1;
tod %= TOD_4YEARS;
if((leapyear = tod / TOD_YEAR) == 4)
year--;
year += leapyear;
}
else
year = 0;
if(epoch > 0)
return (((year % 4) != 0) && (((year % 4) - (epoch % 4)) <= 0)) ? -TOD_DAY : 0;
else
return (((year % 4) == 0 && (-epoch % 4) != 0) || ((year % 4) + (-epoch % 4) > 4)) ? TOD_DAY : 0;
}
DLL_EXPORT char *config_cnslport = "3270";
/*-------------------------------------------------------------------*/
/* Function to build system configuration */
/*-------------------------------------------------------------------*/
void build_config (char *fname)
{
int rc; /* Return code */
int i; /* Array subscript */
int scount; /* Statement counter */
int cpu; /* CPU number */
int count; /* Counter */
FILE *inc_fp[MAX_INC_LEVEL]; /* Configuration file pointer*/
char *sserial; /* -> CPU serial string */
char *smodel; /* -> CPU model string */
char *sversion; /* -> CPU version string */
char *smainsize; /* -> Main size string */
char *sxpndsize; /* -> Expanded size string */
char *smaxcpu; /* -> Maximum number of CPUs */
char *snumcpu; /* -> Number of CPUs */
char *snumvec; /* -> Number of VFs */
char *sengines; /* -> Processor engine types */
char *ssysepoch; /* -> System epoch */
char *syroffset; /* -> System year offset */
char *stzoffset; /* -> System timezone offset */
char *shercprio; /* -> Hercules base priority */
char *stodprio; /* -> Timer thread priority */
char *scpuprio; /* -> CPU thread priority */
char *sdevprio; /* -> Device thread priority */
char *slogofile; /* -> 3270 logo file */
#if defined(_FEATURE_ECPSVM)
char *secpsvmlevel; /* -> ECPS:VM Keyword */
char *secpsvmlvl; /* -> ECPS:VM level (or 'no')*/
int ecpsvmac; /* -> ECPS:VM add'l arg cnt */
#endif /*defined(_FEATURE_ECPSVM)*/
#if defined(OPTION_SHARED_DEVICES)
char *sshrdport; /* -> Shared device port nbr */
#endif /*defined(OPTION_SHARED_DEVICES)*/
U16 version = 0x00; /* CPU version code */
int dfltver = 1; /* Default version code */
U32 serial; /* CPU serial number */
U16 model; /* CPU model number */
unsigned mainsize; /* Main storage size (MB) */
unsigned xpndsize; /* Expanded storage size (MB)*/
U16 maxcpu; /* Maximum number of CPUs */
U16 numcpu; /* Number of CPUs */
U16 numvec; /* Number of VFs */
#if defined(OPTION_SHARED_DEVICES)
U16 shrdport; /* Shared device port number */
#endif /*defined(OPTION_SHARED_DEVICES)*/
S32 sysepoch; /* System epoch year */
S32 tzoffset; /* System timezone offset */
S32 yroffset; /* System year offset */
S64 ly1960; /* Leap offset for 1960 epoch*/
int hercprio; /* Hercules base priority */
int todprio; /* Timer thread priority */
int cpuprio; /* CPU thread priority */
int devprio; /* Device thread priority */
DEVBLK *dev; /* -> Device Block */
char *sdevnum; /* -> Device number string */
char *sdevtype; /* -> Device type string */
int devtmax; /* Max number device threads */
#if defined(_FEATURE_ECPSVM)
int ecpsvmavail; /* ECPS:VM Available flag */
int ecpsvmlevel; /* ECPS:VM declared level */
#endif /*defined(_FEATURE_ECPSVM)*/
BYTE c; /* Work area for sscanf */
char *styp; /* -> Engine type string */
char *styp_values[] = {"CP","CF","AP","IL","??","IP"}; /* type values */
BYTE ptyp; /* Processor engine type */
#ifdef OPTION_SELECT_KLUDGE
int dummyfd[OPTION_SELECT_KLUDGE]; /* Dummy file descriptors --
this allows the console to
get a low fd when the msg
pipe is opened... prevents
cygwin from thrashing in
select(). sigh */
#endif
char hlogofile[FILENAME_MAX+1] = ""; /* File name from HERCLOGO */
char pathname[MAX_PATH]; /* file path in host format */
/* Initialize SETMODE and set user authority */
SETMODE(INIT);
#ifdef OPTION_SELECT_KLUDGE
/* Reserve some fd's to be used later for the message pipes */
for (i = 0; i < OPTION_SELECT_KLUDGE; i++)
dummyfd[i] = dup(fileno(stderr));
#endif
/* Open the base configuration file */
hostpath(pathname, fname, sizeof(pathname));
inc_level = 0;
inc_fp[inc_level] = fopen (pathname, "r");
if (inc_fp[inc_level] == NULL)
{
logmsg(_("HHCCF003S Open error file %s: %s\n"),
fname, strerror(errno));
delayed_exit(1);
}
inc_stmtnum[inc_level] = 0;
/* Set the default system parameter values */
serial = 0x000001;
model = 0x0586;
mainsize = 2;
xpndsize = 0;
maxcpu = 0;
numcpu = 0;
numvec = MAX_CPU_ENGINES;
sysepoch = 1900;
yroffset = 0;
tzoffset = 0;
#if defined(_390)
sysblk.arch_mode = ARCH_390;
#else
sysblk.arch_mode = ARCH_370;
#endif
#if defined(_900)
sysblk.arch_z900 = ARCH_900;
#endif
sysblk.pgminttr = OS_NONE;
sysblk.timerint = DEFAULT_TIMER_REFRESH_USECS;
#if defined( HTTP_SERVER_CONNECT_KLUDGE )
sysblk.http_server_kludge_msecs = 10;
#endif // defined( HTTP_SERVER_CONNECT_KLUDGE )
hercprio = DEFAULT_HERCPRIO;
todprio = DEFAULT_TOD_PRIO;
cpuprio = DEFAULT_CPU_PRIO;
devprio = DEFAULT_DEV_PRIO;
devtmax = MAX_DEVICE_THREADS;
sysblk.kaidle = KEEPALIVE_IDLE_TIME;
sysblk.kaintv = KEEPALIVE_PROBE_INTERVAL;
sysblk.kacnt = KEEPALIVE_PROBE_COUNT;
#if defined(_FEATURE_ECPSVM)
ecpsvmavail = 0;
ecpsvmlevel = 20;
#endif /*defined(_FEATURE_ECPSVM)*/
#if defined(OPTION_SHARED_DEVICES)
shrdport = 0;
#endif /*defined(OPTION_SHARED_DEVICES)*/
#if defined(_FEATURE_ASN_AND_LX_REUSE)
sysblk.asnandlxreuse = 0; /* ASN And LX Reuse is defaulted to DISABLE */
#endif
#ifdef PANEL_REFRESH_RATE
sysblk.panrate = PANEL_REFRESH_RATE_SLOW;
#endif
/* Initialize locks, conditions, and attributes */
initialize_lock (&sysblk.todlock);
initialize_lock (&sysblk.mainlock);
sysblk.mainowner = LOCK_OWNER_NONE;
initialize_lock (&sysblk.intlock);
initialize_lock (&sysblk.iointqlk);
sysblk.intowner = LOCK_OWNER_NONE;
initialize_lock (&sysblk.sigplock);
// initialize_detach_attr (&sysblk.detattr); // (moved to impl.c)
// initialize_join_attr (&sysblk.joinattr); // (moved to impl.c)
initialize_condition (&sysblk.cpucond);
for (i = 0; i < MAX_CPU_ENGINES; i++)
initialize_lock (&sysblk.cpulock[i]);
initialize_condition (&sysblk.sync_cond);
initialize_condition (&sysblk.sync_bc_cond);
#if defined(OPTION_INSTRUCTION_COUNTING)
initialize_lock (&sysblk.icount_lock);
#endif
#ifdef OPTION_PTTRACE
ptt_trace_init (0, 1);
#endif
#if defined(_FEATURE_MESSAGE_SECURITY_ASSIST)
/* Initialize the wrapping key registers lock */
initialize_lock(&sysblk.wklock);
#endif /*defined(_FEATURE_MESSAGE_SECURITY_ASSIST)*/
#if defined(OPTION_FISHIO)
InitIOScheduler // initialize i/o scheduler...
(
sysblk.arch_mode, // (for calling execute_ccw_chain)
&sysblk.devprio, // (ptr to device thread priority)
MAX_DEVICE_THREAD_IDLE_SECS, // (maximum device thread wait time)
devtmax // (maximum #of device threads allowed)
);
#else // !defined(OPTION_FISHIO)
initialize_lock (&sysblk.ioqlock);
initialize_condition (&sysblk.ioqcond);
/* Set max number device threads */
sysblk.devtmax = devtmax;
sysblk.devtwait = sysblk.devtnbr =
sysblk.devthwm = sysblk.devtunavail = 0;
#endif // defined(OPTION_FISHIO)
/* Default the licence setting */
losc_set(PGM_PRD_OS_RESTRICTED);
/* Default CPU type CP */
for (i = 0; i < MAX_CPU_ENGINES; i++)
sysblk.ptyp[i] = SCCB_PTYP_CP;
/* Cap the default priorities at zero if setuid not available */
#if !defined(NO_SETUID)
if (sysblk.suid != 0)
{
#endif /*!defined(NO_SETUID)*/
if (hercprio < 0)
hercprio = 0;
if (todprio < 0)
todprio = 0;
if (cpuprio < 0)
cpuprio = 0;
if (devprio < 0)
devprio = 0;
#if !defined(NO_SETUID)
}
#endif /*!defined(NO_SETUID)*/
/*****************************************************************/
/* Parse configuration file system parameter statements... */
/*****************************************************************/
for (scount = 0; ; scount++)
{
/* Read next record from the configuration file */
while (inc_level >= 0 && read_config (fname, inc_fp[inc_level]))
{
fclose (inc_fp[inc_level--]);
}
if (inc_level < 0)
{
logmsg(_("HHCCF004S No device records in file %s\n"),
fname);
delayed_exit(1);
}
#if defined( OPTION_ENHANCED_CONFIG_INCLUDE )
if (strcasecmp (keyword, "ignore") == 0)
{
if (strcasecmp (operand, "include_errors") == 0)
{
logmsg( _("HHCCF081I %s Will ignore include errors .\n"),
fname);
inc_ignore_errors = 1 ;
}
continue ;
}
/* Check for include statement */
if (strcasecmp (keyword, "include") == 0)
{
if (++inc_level >= MAX_INC_LEVEL)
{
logmsg(_( "HHCCF082S Error in %s line %d: "
"Maximum nesting level (%d) reached\n"),
fname, inc_stmtnum[inc_level-1], MAX_INC_LEVEL);
delayed_exit(1);
}
logmsg( _("HHCCF083I %s Including %s at %d.\n"),
fname, operand, inc_stmtnum[inc_level-1]);
hostpath(pathname, operand, sizeof(pathname));
inc_fp[inc_level] = fopen (pathname, "r");
if (inc_fp[inc_level] == NULL)
{
inc_level--;
if ( inc_ignore_errors == 1 )
{
logmsg(_("HHCCF084W %s Open error ignored file %s: %s\n"),
fname, operand, strerror(errno));
continue ;
}
else
{
logmsg(_("HHCCF085S %s Open error file %s: %s\n"),
fname, operand, strerror(errno));
delayed_exit(1);
}
}
inc_stmtnum[inc_level] = 0;
continue;
}
#endif // defined( OPTION_ENHANCED_CONFIG_INCLUDE )
/* Exit loop if first device statement found */
if (strlen(keyword) <= 4
&& sscanf(keyword, "%x%c", &rc, &c) == 1)
break;
/* ISW */
/* Also exit if keyword contains '-', ',' or '.' */
/* Added because device statements may now be a compound device number specification */
if(strchr(keyword,'-'))
{
break;
}
if(strchr(keyword,'.'))
{
break;
}
if(strchr(keyword,','))
{
break;
}
/* Also exit if keyword contains ':' (added by Harold Grovesteen jan2008) */
/* Added because device statements may now contain channel set or LCSS id */
if(strchr(keyword,':'))
{
break;
}
/* Clear the operand value pointers */
sserial = NULL;
smodel = NULL;
sversion = NULL;
smainsize = NULL;
sxpndsize = NULL;
smaxcpu = NULL;
snumcpu = NULL;
snumvec = NULL;
sengines = NULL;
ssysepoch = NULL;
syroffset = NULL;
stzoffset = NULL;
shercprio = NULL;
stodprio = NULL;
scpuprio = NULL;
sdevprio = NULL;
slogofile = NULL;
#if defined(_FEATURE_ECPSVM)
secpsvmlevel = NULL;
secpsvmlvl = NULL;
ecpsvmac = 0;
#endif /*defined(_FEATURE_ECPSVM)*/
#if defined(OPTION_SHARED_DEVICES)
sshrdport = NULL;
#endif /*defined(OPTION_SHARED_DEVICES)*/
/* Check for old-style CPU statement */
if (scount == 0 && addargc == 5 && strlen(keyword) == 6
&& sscanf(keyword, "%x%c", &rc, &c) == 1)
{
sserial = keyword;
smodel = operand;
smainsize = addargv[0];
sxpndsize = addargv[1];
config_cnslport = strdup(addargv[2]);
snumcpu = addargv[3];
set_loadparm(addargv[4]);
}
else
{
if (strcasecmp (keyword, "cpuserial") == 0)
{
sserial = operand;
}
else if (strcasecmp (keyword, "cpumodel") == 0)
{
smodel = operand;
}
else if (strcasecmp (keyword, "mainsize") == 0)
{
smainsize = operand;
}
else if (strcasecmp (keyword, "xpndsize") == 0)
{
sxpndsize = operand;
}
else if (strcasecmp (keyword, "cnslport") == 0)
{
config_cnslport = strdup(operand);
}
else if (strcasecmp (keyword, "maxcpu") == 0)
{
smaxcpu = operand;
}
else if (strcasecmp (keyword, "numcpu") == 0)
{
snumcpu = operand;
}
else if (strcasecmp (keyword, "numvec") == 0)
{
snumvec = operand;
}
else if (strcasecmp (keyword, "engines") == 0)
{
sengines = operand;
}
else if (strcasecmp (keyword, "sysepoch") == 0)
{
ssysepoch = operand;
if (addargc > 0)
{
syroffset = addargv[0];
addargc--;
}
}
else if (strcasecmp (keyword, "yroffset") == 0)
{
syroffset = operand;
}
else if (strcasecmp (keyword, "tzoffset") == 0)
{
stzoffset = operand;
}
else if (strcasecmp (keyword, "cpuverid") == 0)
{
sversion = operand;
}
else if (strcasecmp (keyword, "hercprio") == 0)
{
shercprio = operand;
}
else if (strcasecmp (keyword, "todprio") == 0)
{
stodprio = operand;
}
else if (strcasecmp (keyword, "cpuprio") == 0)
{
scpuprio = operand;
}
else if (strcasecmp (keyword, "devprio") == 0)
{
sdevprio = operand;
}
else if (strcasecmp (keyword, "logofile") == 0)
{
logmsg(_("HHCCF061W Warning in %s line %d: "
"LOGOFILE statement deprecated. Use HERCLOGO instead\n"),
fname, inc_stmtnum[inc_level]);
slogofile=operand;
}
else if (strcasecmp (keyword, "herclogo") == 0)
{
slogofile=operand;
}
#if defined(_FEATURE_ECPSVM)
/* ECPS:VM support */
else if(strcasecmp(keyword, "ecps:vm") == 0)
{
secpsvmlevel=operand;
secpsvmlvl=addargv[0];
ecpsvmac=addargc;
logmsg(_("HHCCF061W Warning in %s line %d: "
"ECPS:VM Statement deprecated. Use ECPSVM instead\n"),
fname, inc_stmtnum[inc_level]);
addargc=0;
}
else if(strcasecmp(keyword, "ecpsvm") == 0)
{
secpsvmlevel=operand;
secpsvmlvl=addargv[0];
ecpsvmac=addargc;
addargc=0;
}
#endif /*defined(_FEATURE_ECPSVM)*/
#if defined(OPTION_SHARED_DEVICES)
else if (strcasecmp (keyword, "shrdport") == 0)
{
sshrdport = operand;
}
#endif /*defined(OPTION_SHARED_DEVICES)*/
else
{
logmsg( _("HHCCF008E Error in %s line %d: "
"Syntax error: %s\n"),
fname, inc_stmtnum[inc_level], keyword);
operand = "";
addargc = 0;
}
/* Check for one and only one operand */
if (operand == NULL || addargc != 0)
{
logmsg( _("HHCCF009E Error in %s line %d: "
"Incorrect number of operands\n"),
fname, inc_stmtnum[inc_level]);
}
} /* end else (not old-style CPU statement) */
/* Parse CPU version number operand */
if (sversion != NULL)
{
if (strlen(sversion) != 2
|| sscanf(sversion, "%hx%c", &version, &c) != 1
|| version>255)
{
logmsg(_("HHCCF012S Error in %s line %d: "
"%s is not a valid CPU version code\n"),
fname, inc_stmtnum[inc_level], sversion);
delayed_exit(1);
}
dfltver = 0;
}
/* Parse CPU serial number operand */
if (sserial != NULL)
{
if (strlen(sserial) != 6
|| sscanf(sserial, "%x%c", &serial, &c) != 1)
{
logmsg(_("HHCCF051S Error in %s line %d: "
"%s is not a valid serial number\n"),
fname, inc_stmtnum[inc_level], sserial);
delayed_exit(1);
}
}
/* Parse CPU model number operand */
if (smodel != NULL)
{
if (strlen(smodel) != 4
|| sscanf(smodel, "%hx%c", &model, &c) != 1)
{
logmsg(_("HHCCF012S Error in %s line %d: "
"%s is not a valid CPU model\n"),
fname, inc_stmtnum[inc_level], smodel);
delayed_exit(1);
}
}
/* Parse main storage size operand */
if (smainsize != NULL)
{
if (sscanf(smainsize, "%u%c", &mainsize, &c) != 1
|| mainsize < 2
|| (mainsize > 4095 && sizeof(sysblk.mainsize) < 8)
|| (mainsize > 4095 && sizeof(size_t) < 8))
{
logmsg(_("HHCCF013S Error in %s line %d: "
"Invalid main storage size %s\n"),
fname, inc_stmtnum[inc_level], smainsize);
delayed_exit(1);
}
}
/* Parse expanded storage size operand */
if (sxpndsize != NULL)
{
if (sscanf(sxpndsize, "%u%c", &xpndsize, &c) != 1
|| xpndsize > (0x100000000ULL / XSTORE_PAGESIZE) - 1
|| (xpndsize > 4095 && sizeof(size_t) < 8))
{
logmsg(_("HHCCF014S Error in %s line %d: "
"Invalid expanded storage size %s\n"),
fname, inc_stmtnum[inc_level], sxpndsize);
delayed_exit(1);
}
}
/* Parse Hercules priority operand */
if (shercprio != NULL)
if (sscanf(shercprio, "%d%c", &hercprio, &c) != 1)
{
logmsg(_("HHCCF016S Error in %s line %d: "
"Invalid Hercules process group thread priority %s\n"),
fname, inc_stmtnum[inc_level], shercprio);
delayed_exit(1);
}
#if !defined(NO_SETUID)
if(sysblk.suid != 0 && hercprio < 0)
{
logmsg(_("HHCCF017W Hercules is not running as setuid root, "
"cannot raise Hercules process group thread priority\n"));
hercprio = 0; /* Set priority to Normal */
}
#endif /*!defined(NO_SETUID)*/
sysblk.hercprio = hercprio;
/* Parse TOD Clock priority operand */
if (stodprio != NULL)
if (sscanf(stodprio, "%d%c", &todprio, &c) != 1)
{
logmsg(_("HHCCF016S Error in %s line %d: "
"Invalid TOD Clock thread priority %s\n"),
fname, inc_stmtnum[inc_level], stodprio);
delayed_exit(1);
}
#if !defined(NO_SETUID)
if(sysblk.suid != 0 && todprio < 0)
{
logmsg(_("HHCCF017W Hercules is not running as setuid root, "
"cannot raise TOD Clock thread priority\n"));
todprio = 0; /* Set priority to Normal */
}
#endif /*!defined(NO_SETUID)*/
sysblk.todprio = todprio;
/* Parse CPU thread priority operand */
if (scpuprio != NULL)
if (sscanf(scpuprio, "%d%c", &cpuprio, &c) != 1)
{
logmsg(_("HHCCF016S Error in %s line %d: "
"Invalid CPU thread priority %s\n"),
fname, inc_stmtnum[inc_level], scpuprio);
delayed_exit(1);
}
#if !defined(NO_SETUID)
if(sysblk.suid != 0 && cpuprio < 0)
{
logmsg(_("HHCCF017W Hercules is not running as setuid root, "
"cannot raise CPU priority\n"));
cpuprio = 0; /* Set priority to Normal */
}
#endif /*!defined(NO_SETUID)*/
sysblk.cpuprio = cpuprio;
/* Parse Device thread priority operand */
if (sdevprio != NULL)
if (sscanf(sdevprio, "%d%c", &devprio, &c) != 1)
{
logmsg(_("HHCCF016S Error in %s line %d: "
"Invalid device thread priority %s\n"),
fname, inc_stmtnum[inc_level], sdevprio);
delayed_exit(1);
}
#if !defined(NO_SETUID)
if(sysblk.suid != 0 && devprio < 0)
logmsg(_("HHCCF017W Hercules is not running as setuid root, "
"cannot raise device thread priority\n"));
#endif /*!defined(NO_SETUID)*/
sysblk.devprio = devprio;
/* Parse Device thread priority operand */
if (sdevprio != NULL)
if (sscanf(sdevprio, "%d%c", &devprio, &c) != 1)
{
logmsg(_("HHCCF016S Error in %s line %d: "
"Invalid device thread priority %s\n"),
fname, inc_stmtnum[inc_level], sdevprio);
delayed_exit(1);
}
#if !defined(NO_SETUID)
if(sysblk.suid != 0 && devprio < 0)
logmsg(_("HHCCF017W Hercules is not running as setuid root, "
"cannot raise device thread priority\n"));
#endif /*!defined(NO_SETUID)*/
sysblk.devprio = devprio;
/* Parse maximum number of CPUs operand */
if (smaxcpu != NULL)
{
if (sscanf(smaxcpu, "%hu%c", &maxcpu, &c) != 1
|| maxcpu < 1
|| maxcpu > MAX_CPU_ENGINES)
{
fprintf(stderr, _("HHCCF021S Error in %s line %d: "
"Invalid maximum number of CPUs %s\n"),
fname, inc_stmtnum[inc_level], smaxcpu);
delayed_exit(1);
}
}
/* Parse number of CPUs operand */
if (snumcpu != NULL)
{
if (sscanf(snumcpu, "%hu%c", &numcpu, &c) != 1
|| numcpu > MAX_CPU_ENGINES)
{
logmsg(_("HHCCF018S Error in %s line %d: "
"Invalid number of CPUs %s\n"),
fname, inc_stmtnum[inc_level], snumcpu);
delayed_exit(1);
}
}
sysblk.numcpu = numcpu ? numcpu : 1;
/* Parse number of VFs operand */
if (snumvec != NULL)
{
#ifdef _FEATURE_VECTOR_FACILITY
if (sscanf(snumvec, "%hu%c", &numvec, &c) != 1
|| numvec > MAX_CPU_ENGINES)
{
logmsg(_("HHCCF019S Error in %s line %d: "
"Invalid number of VFs %s\n"),
fname, inc_stmtnum[inc_level], snumvec);
delayed_exit(1);
}
#else /*!_FEATURE_VECTOR_FACILITY*/
logmsg(_("HHCCF020W Vector Facility support not configured\n"));
#endif /*!_FEATURE_VECTOR_FACILITY*/
}
sysblk.numvec = numvec;
/* Parse processor engine types operand */
/* example: ENGINES 4*CP,AP,2*IP */
if (sengines != NULL)
{
styp = strtok(sengines,",");
for (cpu = 0; styp != NULL; )
{
count = 1;
if (isdigit(styp[0]))
{
if (sscanf(styp, "%d%c", &count, &c) != 2
|| c != '*' || count < 1)
{
logmsg(_("HHCCF074S Error in %s line %d: "
"Invalid engine syntax %s\n"),
fname, inc_stmtnum[inc_level], styp);
delayed_exit(1);
break;
}
styp = strchr(styp,'*') + 1;
}
if (strcasecmp(styp,"cp") == 0)
ptyp = SCCB_PTYP_CP;
else if (strcasecmp(styp,"cf") == 0)
ptyp = SCCB_PTYP_ICF;
else if (strcasecmp(styp,"il") == 0)
ptyp = SCCB_PTYP_IFL;
else if (strcasecmp(styp,"ap") == 0)
ptyp = SCCB_PTYP_IFA;
else if (strcasecmp(styp,"ip") == 0)
ptyp = SCCB_PTYP_SUP;
else {
logmsg(_("HHCCF075S Error in %s line %d: "
"Invalid engine type %s\n"),
fname, inc_stmtnum[inc_level], styp);
delayed_exit(1);
break;
}
while (count-- > 0 && cpu < MAX_CPU_ENGINES)
{
logmsg("HHCCF077I Engine %d set to type %d (%s)\n",
cpu, ptyp, styp_values[ptyp]);
sysblk.ptyp[cpu++] = ptyp;
}
styp = strtok(NULL,",");
}
}
/* Parse system epoch operand */
if (ssysepoch != NULL)
{
if (strlen(ssysepoch) != 4
|| sscanf(ssysepoch, "%d%c", &sysepoch, &c) != 1
|| sysepoch <= 1800 || sysepoch >= 2100)
{
logmsg(_("HHCCF022S Error in %s line %d: "
"%s is not a valid system epoch.\n"
" The only valid values are "
"1801-2099\n"),
fname, inc_stmtnum[inc_level], ssysepoch);
delayed_exit(1);
}
}
/* Parse year offset operand */
if (syroffset != NULL)
{
if (sscanf(syroffset, "%d%c", &yroffset, &c) != 1
|| (yroffset < -142) || (yroffset > 142))
{
logmsg(_("HHCCF070S Error in %s line %d: "
"%s is not a valid year offset\n"),
fname, inc_stmtnum[inc_level], syroffset);
delayed_exit(1);
}
}
/* Parse timezone offset operand */
if (stzoffset != NULL)
{
if (strlen(stzoffset) != 5
|| sscanf(stzoffset, "%d%c", &tzoffset, &c) != 1
|| (tzoffset < -2359) || (tzoffset > 2359))
{
logmsg(_("HHCCF023S Error in %s line %d: "
"%s is not a valid timezone offset\n"),
fname, inc_stmtnum[inc_level], stzoffset);
delayed_exit(1);
}
}
/* Parse terminal logo option */
if (slogofile != NULL)
{
strncpy(hlogofile, slogofile, sizeof(hlogofile)-1);
hlogofile[sizeof(hlogofile)-1] = '\0';
}
#if defined(_FEATURE_ECPSVM)
/* Parse ECPS:VM level */
if(secpsvmlevel != NULL)
{
while(1) /* Dummy while loop for break support */
{
ecpsvmavail=0;
ecpsvmlevel=0;
if(strcasecmp(secpsvmlevel,"no")==0)
{
ecpsvmavail=0;
break;
}
if(strcasecmp(secpsvmlevel,"yes")==0)
{
ecpsvmavail=1;
ecpsvmlevel=20;
break;
}
if(strcasecmp(secpsvmlevel,"level")==0)
{
ecpsvmavail=1;
if(ecpsvmac==0)
{
logmsg(_("HHCCF062W Warning in %s line %d: "
"Missing ECPSVM level value. 20 Assumed\n"),
fname, inc_stmtnum[inc_level]);
ecpsvmavail=1;
ecpsvmlevel=20;
break;
}
if (sscanf(secpsvmlvl, "%d%c", &ecpsvmlevel, &c) != 1)
{
logmsg(_("HHCCF051W Warning in %s line %d: "
"Invalid ECPSVM level value : %s. 20 Assumed\n"),
fname, inc_stmtnum[inc_level], secpsvmlevel);
ecpsvmavail=1;
ecpsvmlevel=20;
break;
}
break;
}
ecpsvmavail=1;
if (sscanf(secpsvmlevel, "%d%c", &ecpsvmlevel, &c) != 1)
{
logmsg(_("HHCCF051W Error in %s line %d: "
"Invalid ECPSVM keyword : %s. NO Assumed\n"),
fname, inc_stmtnum[inc_level], secpsvmlevel);
ecpsvmavail=0;
ecpsvmlevel=0;
break;
}
else
{
logmsg(_("HHCCF063W Warning in %s line %d: "
"Specifying ECPSVM level directly is deprecated. Use the 'LEVEL' keyword instead.\n"),
fname, inc_stmtnum[inc_level]);
break;
}
break;
}
sysblk.ecpsvm.available=ecpsvmavail;
sysblk.ecpsvm.level=ecpsvmlevel;
}
#endif /*defined(_FEATURE_ECPSVM)*/
#if defined(OPTION_SHARED_DEVICES)
/* Parse shared device port number operand */
if (sshrdport != NULL)
{
if (sscanf(sshrdport, "%hu%c", &shrdport, &c) != 1
|| shrdport < 1024 )
{
logmsg(_("HHCCF029S Error in %s line %d: "
"Invalid SHRDPORT port number %s\n"),
fname, inc_stmtnum[inc_level], sshrdport);
delayed_exit(1);
}
}
#endif /*defined(OPTION_SHARED_DEVICES)*/
} /* end for(scount) (end of configuration file statement loop) */
/* Read the logofile */
if (sysblk.logofile == NULL) /* LogoFile NOT passed in command line */
{
if (hlogofile[0] != '\0') /* LogoFile SET in hercules config */
{
readlogo(hlogofile);
}
else /* Try to Read Logo File using Default FileName */
{
slogofile=getenv("HERCLOGO");
if (slogofile==NULL)
{
readlogo("herclogo.txt");
}
else
{
readlogo(slogofile);
}
} /* Otherwise Use Internal LOGO */
}
else /* LogoFile passed in command line */
{
readlogo(sysblk.logofile);
}
#if defined( OPTION_TAPE_AUTOMOUNT )
/* Define default AUTOMOUNT directory if needed */
if (sysblk.tamdir && sysblk.defdir == NULL)
{
char cwd[ MAX_PATH ];
TAMDIR *pNewTAMDIR = malloc( sizeof(TAMDIR) );
if (!pNewTAMDIR)
{
logmsg( _("HHCCF900S Out of memory!\n"));
delayed_exit(1);
}
VERIFY( getcwd( cwd, sizeof(cwd) ) != NULL );
rc = strlen( cwd );
if (cwd[rc-1] != *PATH_SEP)
strlcat (cwd, PATH_SEP, sizeof(cwd));
pNewTAMDIR->dir = strdup (cwd);
pNewTAMDIR->len = strlen (cwd);
pNewTAMDIR->rej = 0;
pNewTAMDIR->next = sysblk.tamdir;
sysblk.tamdir = pNewTAMDIR;
sysblk.defdir = pNewTAMDIR->dir;
logmsg(_("HHCCF090I Default Allowed AUTOMOUNT directory = \"%s\"\n"),
sysblk.defdir);
}
#endif /* OPTION_TAPE_AUTOMOUNT */
/* Set root mode in order to set priority */
SETMODE(ROOT);
/* Set Hercules base priority */
if (setpriority(PRIO_PGRP, 0, hercprio))
logmsg (_("HHCCF064W Hercules set priority %d failed: %s\n"),
hercprio, strerror(errno));
/* Back to user mode */
SETMODE(USER);
/* Display Hercules thread information on control panel */
logmsg (_("HHCCF065I Hercules: tid="TIDPAT", pid=%d, pgid=%d, "
"priority=%d\n"),
thread_id(), getpid(), getpgrp(),
getpriority(PRIO_PGRP,0));
#if defined(OPTION_SHARED_DEVICES)
sysblk.shrdport = shrdport;
#endif /*defined(OPTION_SHARED_DEVICES)*/
#if defined(_370) || defined(_390)
if(dfltver)
version =
#if defined(_900)
(sysblk.arch_mode == ARCH_900) ? 0x00 :
#endif
0xFD;
#endif
/* Build CPU identifier */
sysblk.cpuid = ((U64)version << 56)
| ((U64)serial << 32)
| ((U64)model << 16);
/* Reset the clock steering registers */
csr_reset();
/* Set up the system TOD clock offset: compute the number of
* microseconds offset to 0000 GMT, 1 January 1900 */
if(sysepoch != 1900 && sysepoch != 1960)
{
if(sysepoch < 1960)
logmsg(_("HHCCF072W SYSEPOCH %04d is deprecated. "
"Please specify \"SYSEPOCH 1900 %s%d\".\n"),
sysepoch, 1900-sysepoch > 0 ? "+" : "", 1900-sysepoch);
else
logmsg(_("HHCCF073W SYSEPOCH %04d is deprecated. "
"Please specify \"SYSEPOCH 1960 %s%d\".\n"),
sysepoch, 1960-sysepoch > 0 ? "+" : "", 1960-sysepoch);
}
if(sysepoch == 1960 || sysepoch == 1988)
ly1960 = TOD_DAY;
else
ly1960 = 0;
sysepoch -= 1900 + yroffset;
set_tod_epoch(((sysepoch*365+(sysepoch/4))*-TOD_DAY)+lyear_adjust(sysepoch)+ly1960);
sysblk.sysepoch = sysepoch;
/* Set the timezone offset */
adjust_tod_epoch((tzoffset/100*3600+(tzoffset%100)*60)*16000000LL);
/* Gabor Hoffer (performance option) */
copy_opcode_tables();
/*****************************************************************/
/* Parse configuration file device statements... */
/*****************************************************************/
while(1)
{
/* First two fields are device number and device type */
sdevnum = keyword;
sdevtype = operand;
if (sdevnum == NULL || sdevtype == NULL)
{
logmsg(_("HHCCF035S Error in %s line %d: "
"Missing device number or device type\n"),
fname, inc_stmtnum[inc_level]);
delayed_exit(1);
}
/* Parse devnum */
rc=parse_and_attach_devices(sdevnum,sdevtype,addargc,addargv);
if(rc==-2)
{
logmsg(_("HHCCF036S Error in %s line %d: "
"%s is not a valid device number(s) specification\n"),
fname, inc_stmtnum[inc_level], sdevnum);
delayed_exit(1);
}
/* Read next device record from the configuration file */
#if defined( OPTION_ENHANCED_CONFIG_INCLUDE )
while (1)
{
while (inc_level >= 0 && read_config (fname, inc_fp[inc_level]) )
{
fclose (inc_fp[inc_level--]);
}
if (inc_level < 0 || strcasecmp (keyword, "include") != 0)
break;
if (++inc_level >= MAX_INC_LEVEL)
{
logmsg(_( "HHCCF082S Error in %s line %d: "
"Maximum nesting level (%d) reached\n"),
fname, inc_stmtnum[inc_level-1], MAX_INC_LEVEL);
delayed_exit(1);
}
logmsg( _("HHCCF083I %s Including %s at %d .\n"),
fname, operand, inc_stmtnum[inc_level-1]);
hostpath(pathname, operand, sizeof(pathname));
inc_fp[inc_level] = fopen (pathname, "r");
if (inc_fp[inc_level] == NULL)
{
inc_level--;
if ( inc_ignore_errors == 1 )
{
logmsg(_("HHCCF084W %s Open error ignored file %s: %s\n"),
fname, operand, strerror(errno));
continue ;
}
else
{
logmsg(_("HHCCF085E %s Open error file %s: %s\n"),
fname, operand, strerror(errno));
delayed_exit(1);
}
}
inc_stmtnum[inc_level] = 0;
continue;
}
if (inc_level < 0)
#else // !defined( OPTION_ENHANCED_CONFIG_INCLUDE )
if (read_config (fname, inc_fp[inc_level]))
#endif // defined( OPTION_ENHANCED_CONFIG_INCLUDE )
break;
} /* end while(1) */
#if !defined( OPTION_ENHANCED_CONFIG_INCLUDE )
/* close configuration file */
rc = fclose(inc_fp[inc_level]);
#endif // !defined( OPTION_ENHANCED_CONFIG_INCLUDE )
/* Now configure storage. We do this after processing the device
* statements so the fork()ed hercifc process won't require as much
* virtual storage. We will need to update all the devices too.
*/
config_storage(mainsize, xpndsize);
for (dev = sysblk.firstdev; dev; dev = dev->nextdev)
{
dev->mainstor = sysblk.mainstor;
dev->storkeys = sysblk.storkeys;
dev->mainlim = sysblk.mainsize - 1;
}
#if defined(_FEATURE_REGION_RELOCATE)
/* Initialize base zone storage view (SIE compat) */
for(i = 0; i < FEATURE_SIE_MAXZONES; i++)
{
sysblk.zpb[i].mso = 0;
sysblk.zpb[i].msl = (sysblk.mainsize - 1) >> 20;
if(sysblk.xpndsize)
{
sysblk.zpb[i].eso = 0;
sysblk.zpb[i].esl = ((size_t)sysblk.xpndsize * XSTORE_PAGESIZE - 1) >> 20;
}
else
{
sysblk.zpb[i].eso = -1;
sysblk.zpb[i].esl = -1;
}
}
#endif
/* Initialize dummy regs.
* Dummy regs are used by the panel or gui when the target cpu
* (sysblk.pcpu) is not configured (ie cpu_thread not started).
*/
sysblk.dummyregs.mainstor = sysblk.mainstor;
sysblk.dummyregs.psa = (PSA*)sysblk.mainstor;
sysblk.dummyregs.storkeys = sysblk.storkeys;
sysblk.dummyregs.mainlim = sysblk.mainsize - 1;
sysblk.dummyregs.dummy = 1;
initial_cpu_reset (&sysblk.dummyregs);
sysblk.dummyregs.arch_mode = sysblk.arch_mode;
sysblk.dummyregs.hostregs = &sysblk.dummyregs;
#ifdef OPTION_SELECT_KLUDGE
/* Release the dummy file descriptors */
for (i = 0; i < OPTION_SELECT_KLUDGE; i++)
close(dummyfd[i]);
#endif
/* Set default maximum number of CPUs */
#ifdef _FEATURE_CPU_RECONFIG
sysblk.maxcpu = sysblk.arch_mode == ARCH_370 ? numcpu : MAX_CPU_ENGINES;
#else
sysblk.maxcpu = numcpu;
#endif /*_FEATURE_CPU_RECONFIG*/
/* Set maximum number of CPUs to specified value */
if (maxcpu > 0) {
sysblk.maxcpu = maxcpu;
}
/* Check that numcpu does not exceed maxcpu */
if (sysblk.numcpu > sysblk.maxcpu) {
logmsg(_("HHCCF086S Error in %s: NUMCPU %d must not exceed MAXCPU %d\n"),
fname, sysblk.numcpu, sysblk.maxcpu);
delayed_exit(1);
}
/* Start the CPUs */
OBTAIN_INTLOCK(NULL);
for(i = 0; i < numcpu; i++)
configure_cpu(i);
RELEASE_INTLOCK(NULL);
} /* end function build_config */
#endif /*!defined(_GEN_ARCH)*/
hercules-3.13/README.TAPE 0000664 0001754 0001754 00000023102 13066015061 011611 0000000 0000000 -------------------------------------------------------------------------------
* Hercules Tape Support Enhancements SPE/Fixes *
* V1.0 - By Ivan S. Warren *
-------------------------------------------------------------------------------
0 - Version History
* 08 Mar 2003 : ISW : Initial Release
I - Supported Device Type emulations :
Device Types supported as of yet :
3410/3411, 3420, 3480, 3490, 9347
Upcoming Device type support :
3422, 3424, 3490E, 3590, 3430, 8809
II - Basic ACF support
The ACF (Automatic Cartridge Feeder) is a feature on Cartridge type tape
drives (3480, 3490, etc..) that automatically loads a new tape when a tape
is removed from the drive. There is no real control over this device by the
host, as it just keeps on feeding tapes one after the other.
Although the ACF feature is unique to cartridge type systems, the emulation
accepts to use the same technique for emulated 1/2 inch tapes reel drives
as well.
ACF is supported as follows :
hercules.cnf syntax :
CUU DEVT @filename
devinit syntax :
devinit CUU @filename
the 'filename' (without the prefixing @) contains a list of files that will
be loaded one after the other. The filenames contained in the file list cannot
describe another ACF file nor an SCSI tape handle (/dev/stX). However, the
files may be standard AWS, HET or OMA files.
To manually reset the ACF to the top of the stack, the devinit can be used
to 'reload' the ACF feature.
If the filename in the ACF description file contains a '*', any option(s) that
follow(s) the '*' (is)are applied to each file, followed by the option(s)
specified on the devinit or hercules.cnf entry, followed by the option(s)
specified on each individual entry.
Example :
hercules.cnf:
180 3420 @newstack compress=1
newstack:
# Sample file
* maxsizeM=16 eotmargin=131072
tape01.aws compress=0
tape02.het maxsizeM=32 eotmargin=65536
tape03.het maxsize=0
This is equivalent to issuing (one at the start and one after each tape unload
event)
180 3420 tape01.aws maxsizeM=16 eotmargin=131072 compress=1 compress=0
devinit 180 tape02.het maxsizeM=16 eotmargin=131072 compress=1 maxsizeM=32 eotmargin=65536
devinit 180 tape03.het maxsizeM=16 eotmargin=131072 compress=1 maxsize=0
Options are processed in the order in which they appear.
Any conflicting parameter overrides the previous one.
For example, on the 1st entry, the resuling "compress" will be 0.
Care must be taken that '*' line entries are all proecessed at once.
For example :
* compress=0
tape01.aws
* compress=1
tape02.aws
is EQUIVALENT to
* compress=0 compress=1
tape01.aws
tape02.aws
NOTE : This may change in the future though, so ACF description files should not rely on this feature.
III - Multivolume support - End of tape indication, Tape file size limitation
Numerous requests have been made in order to support multi-volume tape
handling, as well as limiting the file size generated by any individual
tape file.
Because multivolume support is not necesserally VOL1-HDR1/EOV/EOF based,
a certain number of new features have to be implemented in order to let
the guest program manage the multivolume on it's own.
(ex: VM/DDR, DOS Tape Spooled output, etc..)
Multivolume support resides in the capacity of a drive to indicate to the
controling program that it is about to reach the end of the physical tape
and that measures have to be taken to close the current volume and
request a new media.
3 new options are introduced :
maxsize[K|M]=nnnn :
The resulting file size is limited to the amount specified. maxsize
specifies bytes, maxsizeK specifies a multiple of 10$24 bytes and
maxsizeM specifies a multiple of 1024*1024 bytes. specifying a size
of 0 indicates that there is no limit on the size of the file.
the default is 0 (unlimited file size)
strictsize=0|1 :
Upon reaching the tape file size limit, depending on strictsize,
the tape file will or will not be truncated to enforce the maxsize
limit. The limit is only enforced during a write type operation
(that is : if the file already exists and the program only reads
the file, then the file will NOT be truncated, regardless of the
strictsize setting).
This affects any write that starts BELOW the limit, but that would
extend BEYOND the limit.
This parameter only affects compress HET files. On AWS tapes, the
limit is always enforced, but the file is not truncated (i.e. the
write does not occur, because 1) AWS tapes are never truncated, 2)
the effects of the write are known in advance (no compression)).
Regardless of strictsize, any write operation (Write, Write TM)
will return a Unit Check with Equip Check to the program if the file
size exceeds the predefined limit. If strictsize is 0, the write will
actually have been performed on the tape file. If strictsize is 1,
the file will be truncated on the preceeding tape block boundary.
If an attempt is made to write beyond the maxsize li
Care must be taken that regardless of the 'strictsize' setting,
the tape may become unusable for the guest program should such an
event occur (absence of a Tape Mark for example).
This option has no effect if maxsize is 0
This option only affects HET file tapes
The default is 0 (do not truncate)
eotmargin=nnnn :
This option specifies, in bytes, the threshold before reaching maxsize
during which an indication will be returned to the program to indicate
that an EOT marker has been reached for a write type operation.
The indication of reaching near-capacity is indicated to the program
by presenting Unit Exception in the CSW on a Write type operation,
along with Channel End and Device End.
For certain device types, sense information may also indicate this
information independently of a write operation.
The purpose of this option is to allow the program to determine that
it is time to change to ask for a new tape. For example :
maxsizeM=2 eotmargin=131072
all writes up to 2Mb - 128Kb will occur normally
All writes between 2Mb-128Kb and 2Mb will receive Unit Exception
All writes beyond 2Mb will receive Unit Check
This option has no effect if maxsize is 0
The default is 131072 (128Kb)
Caveats :
If the emulated tape file resides on a disk media that reaches full capacity
before the tape image exceeds it's size limit, the tape emulation will not
detect that situation and will simulate reaching physical end of tape BEFORE
reaching the EOT marker.
This behaviour may be changed at a later time.
IV - Various other changes / Corrections
IV.1 : Device End Suppression for Tape motion CCWs on a non-ready tape drive
IV.2 : Control Unit End is presented on Rewind Unload status
IV.3 : Sense Pending status support
When certain conditions arise during an I/O operation, A sense is
built and Unit Check is presented to the program.
The program is then responsible for retrieving the sense information.
However, if the sense is not the result of a previously occuring
Unit Check, a new sense is built to reflect the current device status.
Also, this management is a necessary step in order to eventually
implement multipath operations (Contengency Allegiance status).
IV.4 : readonly=0|1 :
force an emulated tape device read only.
(1/2 Inch tape ring or 38k Cartridge Protect tab)
(support for this feature is incomplete)
--Ivan
8 Mar 2003
-------------------------------------------------------------------------------
* AUTOMOUNT support *
-------------------------------------------------------------------------------
Starting with Hercules version 3.06 a new AUTOMOUNT option is available
that allows guest operating systems to directly mount, unmount and query
tape device filenames for themselves, without any intervention on the part
of the Hercules operator.
Automount support is enabled via the AUTOMOUNT configuration file statement.
An example guest automount program for VSE called "TMOUNT" is provided in
the util subdirectory of the Hercules source code distribution.
Briefly, the 0x4B (Set Diagnose) CCW is used to mount (or unmount) a file
onto a tape drive, and the 0xE4 (Sense Id) CCW opcode is used to query the
name of the currently mounted file.
For mounts, the 0x4B CCW specifies the filename of the file to be mounted
onto the drive. The file MUST reside in the specified AUTOMOUNT directory
or the automount request will be rejected. To unmount the currently mounted
file, simply do a mount of the special filename "OFFLINE".
To query the name of the currently mounted file, the 0xE4 CCW is used. Note
however that the 0xE4 (Sense Id) CCW opcode cannot be used by itself since
the drive may also already natively support the Sense Id CCW opcode. Instead,
it must be preceded by (command-chained from) a 0x4B CCW with a data transfer
length of one byte. The following 0xE4 command is the one that then specifies
the i/o buffer and buffer length of where the query function is to place the
device's currently mounted host filename.
In summary:
MOUNT: X'4B', , X'20',
UNMOUNT: (same thing but use filename "OFFLINE" instead)
QUERY: X'4B', , X'60', 1
X'E4', , X'20',
Again, please refer to the provided TMOUNT sample for a simple example.
-- Fish
28 May 2008
hercules-3.13/html/ 0000775 0001754 0001754 00000000000 13162135614 011233 5 0000000 0000000 hercules-3.13/html/hercmstm.html 0000664 0001754 0001754 00000002005 13066015061 013654 0000000 0000000
Hercules Version 3: System Messages: TM - tapemap
Hercules Version 3: System Messages: TM - tapemap
This page describes the messages for the Hercules S/370,
ESA/390, and z/Architecture emulator utility program tapemap.
A block read from the member specified is not a multiple of 80
characters long, and so cannot be split evenly into 80-character
card images. The actual length read is length.
Action
Select a different member, or omit the c flag
from the member specification.
hercules-3.13/html/hercmscu.html 0000664 0001754 0001754 00000045163 13066015061 013657 0000000 0000000
Hercules Version 3: System Messages: CU - CCKD Utilities
Hercules Version 3: System Messages: CU - CCKD Utilities
Messages issued by the cckd utility commands (cckdcdsk, cckdcomp and
cckdswap) and cckd utility functions (cckd_chkdsk, cckd_comp and
cckd_swap) are described here. The utility functions are called by
both the utility commands and
Hercules.
Messages
Messages are in the format
HHCCUnnntfile message text
where nnn is the message number, t is either I,
W or E, depending on the severity of the message.
file will either be the part of the file name following the last
slash (/ or \) when called by a utility command, or
will be xxxx: file[n] where xxxx is the device
number and n is the shadow file number when called by Hercules.
The file portion of the message is omitted below for brevity.
The compress function successfully completed and free n
bytes from the file. If n is 0, then the level 2 tables
were repositioned to the beginning of the file in order.
Phase 3 recovery did not complete because one or more trk or blkgrp images
were compressed using a compression (zlib or bzip2) that was
not built into Hercules
Action
Processing terminates. The file has not been updated.
Build Hercules with the missing compression libraries.
Errors have been detected in the compressed file that warrant the
escalation of the check level to level. An additional
explanation reason may be supplied.
The space statistics in the cckddasd device header (cdevhdr) contain
inconsistencies described by code. code is a
16-bit bit field and more than one bit may be on. See cckdutil.c for
the different bit settings.
A header error was found for space (trk or blkgrp) during validation.
id is the trk or blkgrp number. The header is located at file offset offset.
The contents of the 5 byte header is xxxxxxxxxx in hex.
The first byte of the header should be either 00 (compress none),
01 (compress zlib) or 02 (compress bzip2).
For ckd, the next two bytes is the cylinder (in big-endian byte order) and the two
bytes after that is the head (also in big-endian byte order).
For fba, the next four bytes is the block group number (in big-endian byte order).
The header contains an invalid value. Either the offset is incorrect or the header has
been overlaid.
During validation, the header for space (trk or blkgrp) indicates
that the space was compressed using compression (zlib or bzip2) but
support for that compression method was not built into Hercules.
id is the trk or blkgrp number.
Action
Processing continues. However, no recovery will take place.
Build Hercules with the specified compression library.
The space (trk or blkgrp) at offset offset and length length
failed validation.
id is the trk or blkgrp number.
Either the space did not uncompress successfully or the uncompressed space
contains some kind of error.
This error is detected during check level 3 validation.
A read failed at offset offset for length length.
If retcode is not negative then the read was incomplete and the value
indicates how many bytes were read. Otherwise
the text associated with the error number is displayed.
Action
Function processing terminates. Possible Hercules logic error.
Possible hardware error.
Contact the hercules mailing list for assistance.
Issued by
cckdutil.c functions cckd_swapend, cckd_comp and cckd_chkdsk
A write failed at offset offset for length length.
If retcode is not negative then the write was incomplete and the value
indicates how many bytes were written. Otherwise
the text associated with the error number is displayed.
Action
Function processing terminates. Possible Hercules logic error.
Possible hardware error.
Contact the hercules mailing list for assistance.
Issued by
cckdutil.c functions cckd_swapend, cckd_comp and cckd_chkdsk
The device type type from the device header along with
the number of cylinders cyls did not match a table entry
in dasdtab.c. Note that type is the last two bytes of the
device type (eg 90 for a 3390 device type).
Action
Function processing terminates.
Specify the correct file name or manually correct the device header.
The number of L1 table entries nbr1 in the cckd device header
does not match the number calculated nbr2.
The number calculated is the number of cylinders times the number of heads
(ie the number of tracks) divided by 256, rounded up by 1 if there is a
remainder.
Action
Function processing terminates.
Specify the correct file name or manually correct the device headers.
The size of the file size1 is not large enough to contain all L1 table entries;
the size required is size2. The minimum size of a cckd file is
512 + 512 + ( 4 * number of L1 entries)
Action
Function processing terminates.
Specify the correct file name.
During phase 2 recovery, there is not enough space in the maximum file size
to contain the rebuilt L2 tables. This is an unusual situation and probably
indicates some kind of programming error.
Action
Function processing terminates. The file has not been updated.
Contact the hercules mailing list for assistance.
The first 8 bytes of the file did not match an expected identifier.
For a cckd file, the identifier must be either CKD_C370 or
CKD_S370.
For a cfba file, the identifier must be either FBA_C370 or
FBA_S370.
Action
Function processing terminates.
Specify the correct file name.
Issued by
cckdutil.c functions cckd_swapend, cckd_comp and cckd_chkdsk
This program can only be called from Hercules itself, and may not be
executed from the command line.
The program was executed using the name programname.
An attempt to obtain a socket for controlling the destination interface
failed. The error is described by error.
The program was executed using the name programname.
The request from Hercules was invalid. The request code was
request. The request has been ignored.
The program was executed using the name programname.
Action
Make sure that the hercifc program is the same version
as the running copy of Hercules. If so, this is an internal error.
Report it.
An attempt to perform an ioctl operation,
operation, on interface interface
failed. The error is described by error.
The program was executed using the name programname.
hercules-3.13/html/hercmsdu.html 0000664 0001754 0001754 00000074151 13066015061 013657 0000000 0000000
Hercules Version 3: System Messages: DU - DASD Utilities Common
Functions
Hercules Version 3: System Messages: DU - DASD Utilities
Common Functions
This page describes the messages for the Hercules S/370,
ESA/390, and z/Architecture emulator DASD utility programs that are common
to more than one utility.
The track at cylinder number cylinder and head number
head is being rewritten after being modified.
This message is only issued if verbose message reporting has been selected.
The first extent of the dataset is being searched for a key.
The extent starts at the track at cylinder begcyl, head
beghead, and ends at the track at cylinder endcyl,
head endhead.
This message is only issued if verbose message reporting has been selected.
An extent, extent, of the dataset is being searched for a key.
The extent starts at the track at cylinder begcyl, head
beghead, and ends at the track at cylinder endcyl,
head endhead.
This message is only issued if verbose message reporting has been selected.
The file filename is not a valid CKD DASD image file.
Either the first record is not the length of a CKD header record, or the
marker in the header record is not correct.
Action
Supply the name of a valid CKD DASD image file and retry the
operation.
The device type in the CKD header record does not correspond
to any known DASD device. The CKD DASD image file may be corrupt, or
else the device is not supported by Hercules.
Action
Supply the name of a supported CKD DASD image file and retry the
operation.
The device represented by the CKD DASD image file named
filename has heads heads and tracks of
trklen bytes length.
This message is only issued if verbose message reporting has been selected.
During processing of a request to close the CKD image file, the track
at cylinder number cylinder and head number
head is being rewritten, since it has been modified.
This message is only issued if verbose message reporting has been selected.
During processing of a request to close the CKD image file, an
attempt to rewrite a track from the DASD image named
filename failed. The status returned was status.
The device represented by the FBA DASD image file named
filename has sectors sectors of
size bytes length.
This message is only issued if verbose message reporting has been selected.
The volume being processed has the volume
serial serial, and its VTOC format 4 DSCB is at absolute
location cchhr.
This message is only issued if verbose message reporting has been selected.
The VTOC of the volume being processed begins at cylinder and head
begcchh and ends at cylinder and head endcchh.
This message is only issued if verbose message reporting has been selected.
The format 1 DSCB for the requested dataset, dsn, is at
absolute location cchhr.
This message is only issued if verbose message reporting has been selected.
The requested dataset is reported to contain more than three extents
in the format 1 DSCB, but its format 3 DSCB record was not found when an
attempt was made to read it. The VTOC may be corrupt.
A new volume is being created of device type type and volume
serial number serial. It has cylinders cylinders, tracks
tracks per cylinder, and length bytes per track.
An attempt to rewrite the compressed device header record of
the compressed FBA DASD image file named filename failed. The error
is described by error.
Use these commands to unzip the distribution file:
tar xvzf ../hercules-3.13.tar.gz
cd hercules-3.13
Verify you have all of the correct versions of all of the
required packages installed:
./util/bldlvlck
Generate the configure script:
./autogen.sh
Configure Hercules for your system:
./configure
By default, the configure script will attempt to guess appropriate
compiler optimization flags for your system. If its guesses
turn out to be wrong, you can disable all optimization by
passing the --disable-optimization option to
configure, or specify your own optimization flags with
--enable-optimization=FLAGS
For additional configuration options, run: ./configure
--help
Build the executables:
make
Install the programs: as root:
make install
Important:
You must use at least version 3.00 of the gcc compiler and the glibc2 library.
Refer to the
Hercules Frequently-Asked Questions page for required
compiler and other software levels.
Installing pre-built binaries for Mac OS X:
Homebrew may be used to install
Hercules on a Mac with an Intel processor and OS X 10.5 or above.
Homebrew requires Xcode which can be installed from the
App Store.
Install Homebrew using the procedure described
at http://brew.sh/
Enter these commands at a terminal prompt:
brew help brew doctor brew install hercules
You will need to amend the configuration file
hercules.cnf to reflect your device layout and intended
mode of operation (S/370, ESA/390, or z/Architecture).
See the Hercules Configuration File page for
a complete description.
Creating DASD volumes
The Creating Hercules DASD page
describes various methods of creating and loading virtual DASD
volumes. The compressed CKD DASD support is described in this page.
Erases the contents of the command input area.
If the command input area is already empty,
switches to semi-graphical New Panel.
Del
Deletes the character at the cursor position.
Backspace
Erases the previous character.
Insert
Toggles between insert mode and overlay mode.
Tab
Attempts to complete the partial file name at the
cursor position in the command input area. If more
than one possible file exists, a list of matching
file names is displayed.
Home
Moves the cursor to the start of the input in the
command input area. If the command input area is
empty, scrolls the message area to the top.
End
Moves the cursor to the end of the input in the
command input area. If the command input area is
empty, scrolls the message area to the bottom.
Page Up
Scrolls the message area up one screen.
Page Down
Scrolls the message area down one screen.
Up arrow
Recalls previous command into the input area.
Down arrow
Recalls next command into the input area.
Right arrow
Moves cursor to next character of input area.
Left arrow
Moves cursor to previous character of input area.
Ctrl + Up arrow
Scrolls the message area up one line.
Ctrl + Down arrow
Scrolls the message area down one line.
Ctrl + Home
Scrolls the message area to the top.
Ctrl + End
Scrolls the message area to the bottom.
The following additional keyboard functions are effective when the
Hercules Extended Cursor Handling feature (OPTION_EXTCURS) is activated
at compile time.
At present, this feature is activated on the Windows platform only.
Extended cursor handling
Key
Action
Alt + Up arrow
Moves cursor up one row.
Alt + Down arrow
Moves cursor down one row.
Alt + Right arrow
Moves cursor right one column.
Alt + Left arrow
Moves cursor left one column.
Tab
If cursor is outside the command input area,
moves cursor to the start of the input in the
command input area.
Otherwise behaves as described in previous table.
Home
If cursor is outside the command input area,
moves cursor to the start of the input in the
command input area.
Otherwise behaves as described in previous table.
End
If cursor is outside the command input area,
moves cursor to the end of the input in the
command input area.
Otherwise behaves as described in previous table.
Hercules also supports the ability to automatically execute panel commands
upon startup via the 'run-commands' file. If the run-commands file
is found to exist when Hercules starts, each line contained within it is
read and interpreted as a panel command exactly as if the command were
entered from the HMC system console.
The default filename for the run-commands file is "hercules.rc", but may be
overridden by setting the "HERCULES_RC" environment variable to the
desired filename.
Except for the 'pause' command (see paragraph further below), each command
read from the run-commands file is logged to the console preceded by a '> '
(greater-than sign) character so you can easily distinguish between panel
commands entered from the keyboard from those entered via the .rc file.
Lines starting with '#' are treated as "silent comments" and are
thus not logged to the console. Line starting with '*' however are
treated as "loud comments" and will be logged.
In addition to being able to execute any valid panel command (including the
'sh' shell command) via the run-commands file, an additional
'pause nnn' command is supported in order to introduce
a brief delay before reading and processing the next line in the file. The
value nnn can be any number from 1 to 999 and specifies the
number of seconds to delay before reading the next line. Creative use of the
run-commands file can completely automate Hercules startup.
The Hercules Automatic Operator (HAO) feature is a facility which can
automatically issue panel commands in response to specific messages
appearing on the Hercules console.
To use the Hercules Automatic Operator facility, you first define a "rule"
consisting of a "target" and an associated "command". The "target" is
a regular expression pattern used to match against the text of the various
messages that Hercules issues as it runs. Whenever a match is found, the
rule "fires" and its associated command is automatically issued.
The Hercules Automatic Operator facility only operates on messages issued
to the Hercules console. These messages may originate from Hercules itself,
or from the guest operating system via the SCP SYSCONS interface or via the
integrated console printer-keyboard (3215-C or 1052-C). HAO cannot intercept
messages issued by the guest operating system to its own terminals.
Defining a Rule
To define a HAO rule, enter the command:
hao tgt target
to define the rule's "target" match pattern
followed by the command:
hao cmd command
to define the rule's associated panel-command.
The target is a regular expression as defined by your host platform.
When running on Linux, Hercules uses POSIX Extended Regular Expression syntax.
On a Windows platform, regular expression support is provided by
Perl Compatible Regular Expression (PCRE).
The HAO facility can only be used if regular expression support was included
in Hercules at build time.
The associated command is whatever valid Hercules panel command you
wish to issue in response to a message being issued that matches the given
target pattern.
Substituting substrings in the command
The command may contain special variables $1, $2, etc, which will be
replaced by the values of "capturing groups" in the match pattern.
A capturing group is a part of the regular expression enclosed in parentheses
which is matched with text in the target message. In this way, commands may be
constructed which contain substrings extracted from the message which
triggered the command.
The following special variables are recognized:
$1 to $9 -
the text which matched the 1st to 9th capturing
group in the target regular expression
$` - the text preceding the regular expression match
$' - the text following the regular expression match
$$ - replaced by a single dollar sign
Note that substitution of a $n variable does not occur if there are
fewer than n capturing groups in the regular expression.
As an example, the rule below issues the command 'i 001F' in response to
the message HHCTE014I 3270 device 001F client 127.0.0.1 connection reset:
hao tgt HHCTE014I 3270 device ([0-9A-F]{3,4})
hao cmd i $1
Another example, shown below, illustrates how the dot matrix display of a
3480 tape unit might be used to implement an automatic tape library:
To delete a fully or partially defined HAO rule, first use the 'hao list'
command to list all of the defined (or partially defined) rules, and then use
the 'hao del nnn' command to delete the specific rule identified by
nnn (all rules are assigned numbers as they are defined and are thus
identified by their numeric value). Optionally, you can delete all defined or
partially defined rules by issuing the command 'hao clear'.
The current implementation limits the total number of defined rules to 64.
This limit may be raised by increasing the value of the HAO_MAXRULE
constant in hao.c and rebuilding Hercules.
All defined rules are checked for a match each time Hercules issues a message.
There is no way to specify "stop processing subsequent rules". If a message is
issued that matches two or more rules, each associated command is then issued
in sequence.
hercules-3.13/html/hercrnot.html 0000664 0001754 0001754 00000021537 13066015061 013671 0000000 0000000
Hercules Version 3: Release Notes and Known Issues
Hercules Version 3: Release Notes and Known Issues
Release notes for release 3.03
Release date: 20 December 2005
New device types 1052-C and 3215-C
The new integrated console printer-keyboard is emulated on the
hercules console. Commands are sent to the console by means of a command character.
(default '/', thus a logon command is sent by /logon)
Message Security Assist
Starting from release 3.03 the glibcrypt library is no longer needed.
Release notes for release 3.02
Release date: 11 December 2004
ASN-and-LX-reuse facility
This is a new feature of z/Architecture which can cause problems with
certain versions of operating systems running in ARCHLVL=2 mode without
the so-called "Driver 55" fixes. To avoid such problems, specify
ASN_AND_LX_REUSE DISABLE in the configuration file.
Release notes for release 3.01
Release date: 30 November 2003
Library modules and HTTP documents default directories
An error in the 3.00 configuration script caused many users to have to
override the default modules and HTTP documents directory in the
Hercules configuration file, or by setting an environment variable. This
error has been corrected. Hercules also now reports the actual directory
that it uses by default for these files at startup time. If you specified
the MODPATH or HTTPROOT configuration file statements because you
encountered problems, you should examine the messages printed at startup to
see if the default directories are now correct, and remove the statements
if so.
In general, MODPATH and HTTPROOT should not have to be specified
except in unusual circumstances.
Windows default directories
In conjunction with the fix above, the default directories of the
Windows distributed binaries have been changed. The new directories are
under C:\cygwin\usr\local (which is the same as /usr/local under the Cygwin
environment). No action is needed unless you have specified the MODPATH or
HTTPROOT configuration file entries; if so, see the previous note.
Message Security Assist
Support for z990 crypto instructions is conditional on the presence of the
glibcrypt library.
When Hercules is BUILT, the development files for glibcrypt should be available.
When hercules is RUN, the runtime files for glibcrypt should be installed.
Depending on the level of glibcrypt used to *build* hercules, the associated
level of glibcrypt should also be present on the target machine. On systems
supporting shared library versioning, multiple levels of the glibcrypt
runtime libraries can be installed simultaneously, ensuring availability
of the z990 crypto instructions, regardless of the level of glibcrypt with
which hercules was initially built.
CTC and LCS device numbers
CTC and LCS devices now MUST specify ALL addresses on the configuration
statement.
Previously (i.e. with version 3.00), only the first (even numbered) device
needed to be defined and Hercules would automatically define the odd numbered
device for you. Starting with Hercules version 3.01 however, you now need
to define BOTH devices, just like you did with versions prior to 3.00.
Once again, starting with version 3.01, you **MUST** define BOTH DEVICES.
Release notes for release 3.00
Release date: 3 October 2003
CTCI device changes
The CTCI-W32 protocol is deprecated. You should use the CTCI
protocol instead.
The VMNET protocol is also deprecated. Not even its author uses it any
more. Every system on which Hercules is now prebuilt has the TUN/TAP
driver functionality available in one form or another, and it's much more
robust.
Both of these will go away in a future release.
In addition, you must not define both even/odd CTCI device pairs in
your configuration file. You should only define the first even
numbered device. Hercules will automatically define the odd numbered device
for you. If you define the odd numbered device by mistake, an open error will
occur on that device. This is by design. See the README.NETWORKING document
for further details.
Hercules Dynamic Loader support
Starting with version 3.00, Hercules now contains support for the dynamic
loading of certain modules upon startup on Linux, Windows, and Mac OS X.
This support should also work on any platform supported by GNU libtool. As a
result of this new feature, Hercules itself now no longer consists of just the
'hercules.exe' module by itself, but rather consists of both the 'hercules.exe'
program as well as whatever dynamic modules (DLLs) that accompany it.
As a result of this change, whenever you install a new version of Hercules,
you must ensure that you ALSO install the accompanying new versions of the
new dynamic modules as well. Attempting to use a version of Hercules with a
dynamic module that was not specifically built for that version will cause
loading of that dynamic module to fail.
You cannot mix versions of Hercules with differing versions of
dynamically loaded modules.
Ensure that your library path (set by the environment variable
LD_LIBRARY_PATH) set correctly such that it includes the directory of your
Hercules dynamic load libraries. If you see message HHCCF042E,
which indicates that the system is unable to locate necessary loadable
modules, this is likely your problem. This should not be necessary if you
have a binary download, but if you're building from source, especially if
you've previously installed a binary package, this should be the first thing
you do.
ECPS:VM
Do not use ECPS:VM (See README.ECPSVM) in an AP or MP environment
in VM/370. If AP=YES or MP=YES is coded in DMKSYS
and the AP/MP control file is used to build the CP nucleus and
NUMCPU is set to more than 1 in the hercules.cnf
file, any of LOK001, LOK003 or other abends will occur. This occurs because
the Hercules ECPS:VM CP Assist implementation is not MP safe, and
particularly, attempts VM dispatching without holding necessary AP or MP
locks.
Memory allocation on Windows
Due to the change in the "mainstor" memory allocation technique used by Hercules to
address a "double memory consumption" bug in Cygwin's malloc implementation,
some Windows Hercules users may experience an "out of memory" error whenever
Hercules is started with a large MAINSIZE configuration file
value:
HHCCF031S Cannot obtain nnnMB main storage
This error will most likely occur (if it does at all) for those users who
have manually adjusted their Cygwin heap_chunk_in_mb Windows
registry setting value (in order to allow them to specify a large
MAINSIZE value when running Hercules). If this problem does occur
(i.e. if you do happen to experience the above mentioned error with
this new release of Hercules), then either reduce your
heap_chunk_in_mb value (yes, that's correct: reduce it,
as in change it to a smaller value) or else remove it altogether (so
as to let it default).
A complete discussion of this issue is in the RELEASE.NOTES file
in the source distribution.
Thread priority and other problems
There is a known problem with thread priority handling under Mac OS
X. The OS X threading model is different from the one classically used in
Linux. This causes failures to set the timer thread priority, and slow
performance as all of Hercules is set to a low execution priority. This
will be fixed in a future release. A workaround, for now, for slow
performance is to add the statement
CPUPRIO 0
to your Hercules configuration file.
A possibly related problem is that Hercules fails in random ways when
using the NPTL (new POSIX threads library) library under Linux.
This library is used by default in Red Hat 9, and possibly other systems.
If problems are encountered on a very recent version of Linux, try issuing
the command
hercules-3.13/html/hercmsdc.html 0000664 0001754 0001754 00000012601 13066015061 013625 0000000 0000000
Hercules Version 3: System Messages: DC - dasdcopy
Hercules Version 3: System Messages: DC - dasdcopy
This page describes the messages for the Hercules S/370,
ESA/390, and z/Architecture emulator disk image copy utility.
Note that this utility can be called by several names:
dasdcopy, ckd2cckd, cckd2ckd,
fba2cfba, and cfba2fba. The same program is used
for all of these functions. The name used to invoke the program is
reported in each of these messages as progname.
There was no disk drive table entry that matched the number of
cylinders in the CKD source file, size. The program cannot
determine how much data to copy.
There was no disk drive table entry that matched the number of
blocks in the FBA source file, size. The program cannot
determine how much data to copy.
An error was encountered when trying to read a block or track from
the input file named filename. The block or track is
number number.
The status returned is shown as status.
An error was encountered when trying to read a block or track from
the input file named filename. The block or track is number
number.
The status returned is shown as status.
Copyright (C) 1999 Trolltech AS, Norway.
Everyone is permitted to copy and
distribute this license document.
The intent of this license is to establish freedom to share and
change the software regulated by this license under the open source
model.
This license applies to any software containing a notice placed by
the copyright holder saying that it may be distributed under the terms
of the Q Public License version 1.0. Such software is herein referred
to as the Software. This license covers modification and distribution
of the Software, use of third-party application programs based on the
Software, and development of free software which uses the
Software.
Granted Rights
1. You are granted the non-exclusive rights set forth in this
license provided you agree to and comply with any and all conditions
in this license. Whole or partial distribution of the Software, or
software items that link with the Software, in any form signifies
acceptance of this license.
2. You may copy and distribute the Software in unmodified form
provided that the entire package, including - but not restricted to -
copyright, trademark notices and disclaimers, as released by the
initial developer of the Software, is distributed.
3. You may make modifications to the Software and distribute your
modifications, in a form that is separate from the Software, such as
patches. The following restrictions apply to modifications:
a. Modifications must not alter or remove any copyright notices in
the Software.
b. When modifications to the Software are released under this
license, a non-exclusive royalty-free right is granted to the initial
developer of the Software to distribute your modification in future
versions of the Software provided such versions remain available under
these terms in addition to any other license(s) of the initial
developer.
4. You may distribute machine-executable forms of the Software or
machine-executable forms of modified versions of the Software,
provided that you meet these restrictions:
a. You must include this license document in the distribution.
b. You must ensure that all recipients of the machine-executable
forms are also able to receive the complete machine-readable source
code to the distributed Software, including all modifications, without
any charge beyond the costs of data transfer, and place prominent
notices in the distribution explaining this.
c. You must ensure that all modifications included in the
machine-executable forms are available under the terms of this
license.
5. You may use the original or modified versions of the Software to
compile, link and run application programs legally developed by you or
by others.
6. You may develop application programs, reusable components and
other software items that link with the original or modified versions
of the Software. These items, when distributed, are subject to the
following requirements:
a. You must ensure that all recipients of machine-executable forms
of these items are also able to receive and use the complete
machine-readable source code to the items without any charge beyond
the costs of data transfer.
b. You must explicitly license all recipients of your items to use
and re-distribute original and modified versions of the items in both
machine-executable and source code forms. The recipients must be able
to do so without any charges whatsoever, and they must be able to
re-distribute to anyone they choose.
c. If the items are not available to the general public, and the
initial developer of the Software requests a copy of the items, then
you must supply one.
Limitations of Liability
In no event shall the initial developers or copyright holders be
liable for any damages whatsoever, including - but not restricted to -
lost revenue or profits or other direct, indirect, special, incidental
or consequential damages, even if they have been advised of the
possibility of such damages, except to the extent invariable law, if
any, provides otherwise.
No Warranty
The Software and this license document are provided AS IS with NO
WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN,
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.