fis-gtm/sr_unix/gtmio.h

910 lines
31 KiB
C

/****************************************************************
* *
* Copyright 2001, 2012 Fidelity Information Services, Inc *
* *
* This source code contains the intellectual property *
* of its copyright holder(s), and is made available *
* under a license. If you do not know the terms of *
* the license, please stop and do not read further. *
* *
****************************************************************/
/* Define macros to do our IO and restart as appropriate
*
* IOCTL Loop until ioctl call succeeds or fails with other than EINTR.
* OPENFILE Loop until open succeeds or fails with other than EINTR.
* OPENFILE_SYNC
* Loop until open succeeds or fails with other than EINTR.
* Opens with O_DSYNC/O_SYNC in direct mode where possible.
* Else opens only with O_DSYNC or O_SYNC.
* OPEN_OBJECT_FILE
* Opens the object file and waits till it gets a lock(shr/excl).
* Sets default perms if it creates a new file.
* CLOSE_OBJECT_FILE - close the object file after releasing the lock on it.
* CLOSEFILE Loop until close succeeds for fails with other than EINTR.
* CLOSEFILE_RESET
* Loop until close succeeds for fails with other than EINTR.
* At end reset channel to FD_INVALID unconditionally (even if close was not successful).
* CONVERT_OBJECT_LOCK - convert type of lock held on object file
* LSEEKREAD Performs either pread() or an lseek()/ read() combination. In
* the latter case, sets global variable to warn off async IO routines.
* LSEEKREAD_AVAILABLE Same as LSEEKREAD except it has an extra parameter where the number of bytes
* ACTUALLY READ are stored irrespective of whether all REQUESTED BYTES were read or not.
* LSEEKWRITE Same as LSEEKREAD but for WRITE.
* DOREADRC Performs read, returns code 0 if okay, otherwise returns errno.
* DOREADRL Performs read but returns length read or -1 if errno is set.
* DOREADRLTO2 Same as DOREADRL but has a timeout flag to poll on interrupts.
* DOWRITE Performs write with no error checking/return.
* DOWRITERC Performs write, returns code 0 if okay, otherwise returns errno.
* DOWRITERL Performs write but returns length written or -1 if errno is set.
* WRITEPIPE Performs write to FIFO/pipe but individual writes (within loop) limited to system pipe buffer size
*/
#ifndef GTMIO_Included
#define GTMIO_Included
#include <sys/types.h>
#include "gtm_stat.h"
#include "gtm_unistd.h"
#include "gtm_fcntl.h"
#include "eintr_wrappers.h"
#include "min_max.h"
#ifdef __linux__
#include <sys/vfs.h>
#endif
#ifdef KEEP_zOS_EBCDIC
#define DOWRITE_A __write_a
#define DOREAD_A __read_a
#define DOWRITERL_A #error need to create as part of z/OS port and make __write_a return status and good errno
#else
#define DOWRITE_A DOWRITE
#define DOREAD_A read
#define DOWRITERL_A DOWRITERL
#endif
#define MAX_FILE_OPEN_TRIES 20 /* polling count */
#define WAIT_FOR_FILE_TIME 100 /* msec */
#define WAIT_FOR_BLOCK_TIME 100 /* msec */
#define IOCTL(FDESC, REQUEST, ARG, RC) \
{ \
do \
{ \
RC = ioctl(FDESC, REQUEST, ARG); \
} while(-1 == RC && EINTR == errno); \
if (-1 != RC) \
RC = 0; \
else \
RC = errno; \
}
#define OPENFILE(FNAME, FFLAGS, FDESC) \
{ \
do \
{ \
FDESC = OPEN(FNAME, FFLAGS); \
} while(-1 == FDESC && EINTR == errno); \
}
#define OPENFILE3(FNAME, FFLAGS, FMODE, FDESC) \
{ \
do \
{ \
FDESC = OPEN3(FNAME, FFLAGS, FMODE); \
} while(-1 == FDESC && EINTR == errno); \
}
/* OPENFILE4 not needed - io_open_try handles interrupts */
#define FSTYPE_ADVFS "advfs"
#define FSTYPE_UFS "ufs"
#if defined(_AIX)
#define OPENFILE_SYNC(FNAME, FFLAGS, FDESC) OPENFILE(FNAME, FFLAGS | O_DIRECT | O_DSYNC, FDESC);
/* Note: putting the DIRECTIO_FLAG definition before the #if and then redefining it below for the two exceptions
* causes a warning on sparc - "macro redefined: DIRECTIO_FLAG" so it is repeated in each part of the big #define
*/
#define DIRECTIO_FLAG O_DIRECT
#elif defined(__osf__)
#define OPENFILE_SYNC(FNAME, FFLAGS, FDESC) \
{ \
struct statvfs statvfs_buf; \
int macro_errno; \
boolean_t dio_success = TRUE; \
if (-1 == statvfs(FNAME, &statvfs_buf)) \
{ \
macro_errno = errno; \
util_out_print("Error finding FS type for file, !AD :!AD", OPER, \
LEN_AND_STR(FNAME), LEN_AND_STR(STRERROR(macro_errno))); \
dio_success = FALSE; \
} else if (strcmp(FSTYPE_ADVFS, statvfs_buf.f_basetype)) \
dio_success = FALSE; \
if (dio_success) \
{ \
OPENFILE(FNAME, FFLAGS | O_DIRECTIO | O_DSYNC, FDESC); \
} else \
{ \
OPENFILE(FNAME, FFLAGS | O_DSYNC, FDESC); \
} \
}
#define DIRECTIO_FLAG O_DIRECTIO
#elif defined(__sparc)
#define OPENFILE_SYNC(FNAME, FFLAGS, FDESC) \
{ \
struct statvfs statvfs_buf; \
int macro_errno; \
boolean_t dio_success = TRUE; \
if (-1 == statvfs(FNAME, &statvfs_buf)) \
{ \
macro_errno = errno; \
util_out_print("Error finding FS type for file, !AD. !AD", OPER, \
LEN_AND_STR(FNAME), LEN_AND_STR(STRERROR(macro_errno))); \
dio_success = FALSE; \
} else if (strcmp(FSTYPE_UFS, statvfs_buf.f_basetype)) \
dio_success = FALSE; \
OPENFILE(FNAME, FFLAGS | O_DSYNC, FDESC); \
if (dio_success && (FD_INVALID != FDESC)) \
{ \
if (-1 == directio(FDESC, DIRECTIO_ON)) \
{ \
macro_errno = errno; \
util_out_print("Failed to set DIRECT IO option for !AD, reverting to normal IO. !AD ", \
OPER, LEN_AND_STR(FNAME), LEN_AND_STR(STRERROR(macro_errno))); \
} \
} \
}
#define DIRECTIO_FLAG 0
#elif defined(__MVS__)
#define OPENFILE_SYNC(FNAME, FFLAGS, FDESC) OPENFILE(FNAME, FFLAGS | O_SYNC, FDESC);
#define DIRECTIO_FLAG 0
#elif defined(__linux__)
#define OPENFILE_SYNC(FNAME, FFLAGS, FDESC) OPENFILE(FNAME, FFLAGS | O_DIRECT | O_DSYNC, FDESC);
#define DIRECTIO_FLAG O_DIRECT
#elif defined(__hpux)
#define OPENFILE_SYNC(FNAME, FFLAGS, FDESC) OPENFILE(FNAME, FFLAGS | O_DIRECT | O_DSYNC, FDESC);
#define DIRECTIO_FLAG O_DIRECT
#else
#error UNSUPPORTED PLATFORM
#endif
#if defined( __linux__)
/* A special handling was needed for linux due to its inability to lock
* over NFS. The only difference in code is an added check for NFS file
* thru fstatfs
*
* This should ideally include <linux/nfs_fs.h> for NFS_SUPER_MAGIC.
* However, this header file doesn't seem to be standard and gives lots of
* compilation errors and hence defining again here. The constant value
* seems to be portable across all linuxes (courtesy 'statfs' man pages)
*/
#define NFS_SUPER_MAGIC 0x6969
#define LOCK_IS_ALLOWED(FDESC, STATUS) \
{ \
struct statfs buf; \
STATUS = ((-1 != fstatfs(FDESC, &buf)) && (NFS_SUPER_MAGIC != buf.f_type)) ? 0 : -2; \
}
#else
#define LOCK_IS_ALLOWED(FDESC, STATUS) STATUS = 0
#endif
/* The for loop is the workaround for a glitch in read locking in zlink. The primary steps to acquire a
* read-lock are 1. open the file, and 2. read lock it. If a process creates the initial, empty version
* of the file (with the OPEN3), but has not yet write-locked it, and meanwhile, another process does its
* open and gets a read-lock, then a later read within incr_link() will end up reading an empty file. To
* avoid that problem, readers have to poll for a non-empty object file before reading. If the read lock
* is obtained, but the file is empty, then release the read lock, sleep for a while, and retry the file open.
*/
#define OPEN_OBJECT_FILE(FNAME, FFLAG, FDESC) \
{ \
int status; \
struct flock lock; /* arg to lock the file thru fnctl */ \
int cntr; \
struct stat stat_buf; \
pid_t l_pid; \
ZOS_ONLY(int realfiletag;) \
\
l_pid = getpid(); \
for (cntr = 0; cntr < MAX_FILE_OPEN_TRIES; cntr++) \
{ \
while (FD_INVALID == (FDESC = OPEN3(FNAME, FFLAG, 0666)) && EINTR == errno) \
; \
if (-1 != FDESC) \
{ \
LOCK_IS_ALLOWED(FDESC, status); \
if (-2 != status) \
{ \
do { \
lock.l_type = ((O_WRONLY == ((FFLAG) & O_ACCMODE)) || \
( O_RDWR == ((FFLAG) & O_ACCMODE))) ? F_WRLCK : F_RDLCK; \
lock.l_whence = SEEK_SET; /*locking offsets from file beginning*/ \
lock.l_start = lock.l_len = 0; /* lock the whole file */ \
lock.l_pid = l_pid; \
} while (-1 == (status = fcntl(FDESC, F_SETLKW, &lock)) && EINTR == errno); \
} \
if (-1 != status) \
{ \
if ((FFLAG) & O_CREAT) \
{ \
FTRUNCATE(FDESC, 0, status); \
ZOS_ONLY( \
status = gtm_zos_set_tag(FDESC, TAG_BINARY, TAG_NOTTEXT, TAG_FORCE, &realfiletag); \
) \
} else \
{ \
FSTAT_FILE(FDESC, &stat_buf, status); \
if (status || (0 == stat_buf.st_size)) \
{ \
CLOSE_OBJECT_FILE(FDESC, status); \
SHORT_SLEEP(WAIT_FOR_FILE_TIME); \
continue; \
} \
ZOS_ONLY( \
status = gtm_zos_tag_to_policy(FDESC, TAG_BINARY, &realfiletag); \
) \
} \
} \
if (-1 == status) \
CLOSEFILE_RESET(FDESC, status);/* can't fail - no writes, no writes-behind */ \
} \
break; \
} \
}
#define CONVERT_OBJECT_LOCK(FDESC, FFLAG, RC) \
{ \
struct flock lock; /* arg to lock the file thru fnctl */ \
pid_t l_pid; \
\
l_pid = getpid(); \
do { \
lock.l_type = FFLAG; \
lock.l_whence = SEEK_SET; /*locking offsets from file beginning*/ \
lock.l_start = lock.l_len = 0; /* lock the whole file */ \
lock.l_pid = l_pid; \
} while (-1 == (RC = fcntl(FDESC, F_SETLKW, &lock)) && EINTR == errno); \
}
#define CLOSE_OBJECT_FILE(FDESC, RC) \
{ \
struct flock lock; /* arg to unlock the file thru fnctl */ \
do { \
lock.l_type = F_UNLCK; \
lock.l_whence = SEEK_SET; \
lock.l_start = lock.l_len = 0; /* unlock the whole file */ \
lock.l_pid = getpid(); \
} while (-1 == (RC = fcntl(FDESC, F_SETLK, &lock)) && EINTR == errno); \
CLOSEFILE_RESET(FDESC, RC); \
}
#define CLOSEFILE(FDESC, RC) \
{ \
do \
{ \
RC = close(FDESC); \
} while(-1 == RC && EINTR == errno); \
if (-1 == RC) /* Had legitimate error - return it */ \
RC = errno; \
}
#define CLOSEFILE_RESET(FDESC, RC) \
{ \
CLOSEFILE(FDESC, RC) \
FDESC = FD_INVALID; \
}
/* Close file only if we have it open. Use FCNTL to check if we have it open */
#define CLOSEFILE_IF_OPEN(FDESC, RC) \
{ \
int flags; \
\
FCNTL2(FDESC, F_GETFL, flags); \
if ((-1 != flags) || (EBADF != errno)) \
CLOSEFILE(FDESC, RC); /* file is a valid descriptor. Close it */ \
}
#if defined(__osf__) || defined(_AIX) || defined(__sparc) || defined(__linux__) || defined(__hpux) || \
defined(__CYGWIN__) || defined(__MVS__)
/* These platforms are known to support pread/pwrite.
* !!!!!!!!!!!!!! Note !!!!!!!!!!!!!!
* pread and pwrite do NOT (on most platforms) set the file pointer like lseek/read/write would,
* so they are NOT a drop-in replacement !!
*/
#define NOPIO_ONLY(X)
#define GET_LSEEK_FLAG(FDESC, VAL)
/* If definitions are flying around for pread/pread64, don't override them. Otherwise on HPUX
we need to run the 64bit versions of these calls because the POSIX version linkage to the
64 bit variation is broken. SE 07/2005.
*/
#if (defined(__hpux) && !defined(__ia64))
# if !defined(pread) && !defined(pread64)
# define pread pread64
# define pwrite pwrite64
# else
# error "** Interference with pread/pwrite defines - HPUX may have fixed their problem **"
# endif
#endif
#define LSEEKREAD(FDESC, FPTR, FBUFF, FBUFF_LEN, RC) \
{ \
ssize_t gtmioStatus; \
size_t gtmioBuffLen; \
off_t gtmioPtr; \
sm_uc_ptr_t gtmioBuff; \
gtmioBuffLen = FBUFF_LEN; \
gtmioBuff = (sm_uc_ptr_t)(FBUFF); \
gtmioPtr = (off_t)(FPTR); \
for (;;) \
{ \
if (-1 != (gtmioStatus = pread(FDESC, gtmioBuff, gtmioBuffLen, gtmioPtr))) \
{ \
gtmioBuffLen -= gtmioStatus; \
if (0 == gtmioBuffLen || 0 == gtmioStatus) \
break; \
gtmioBuff += gtmioStatus; \
gtmioPtr += gtmioStatus; \
continue; \
} \
if (EINTR != errno) \
break; \
} \
if (0 == gtmioBuffLen) \
RC = 0; \
else if (-1 == gtmioStatus) /* Had legitimate error - return it */ \
RC = errno; \
else \
RC = -1; /* Something kept us from reading what we wanted */ \
}
/* The below macro is almost the same as LSEEKREAD except it has an extra parameter where the number of
* bytes ACTUALLY READ are stored irrespective of whether all REQUESTED BYTES were read or not.
*/
#define LSEEKREAD_AVAILABLE(FDESC, FPTR, FBUFF, FBUFF_LEN, ACTUAL_READLEN, RC) \
{ \
ssize_t gtmioStatus; \
size_t gtmioBuffLen; \
off_t gtmioPtr; \
sm_uc_ptr_t gtmioBuff; \
\
gtmioBuffLen = (FBUFF_LEN); \
gtmioBuff = (sm_uc_ptr_t)(FBUFF); \
gtmioPtr = (off_t)(FPTR); \
for (;;) \
{ \
if (-1 != (gtmioStatus = pread(FDESC, gtmioBuff, gtmioBuffLen, gtmioPtr))) \
{ \
gtmioBuffLen -= gtmioStatus; \
if (0 == gtmioBuffLen || 0 == gtmioStatus) \
break; \
gtmioBuff += gtmioStatus; \
gtmioPtr += gtmioStatus; \
continue; \
} \
if (EINTR != errno) \
break; \
} \
(ACTUAL_READLEN) = (FBUFF_LEN) - gtmioBuffLen; \
if (0 == gtmioBuffLen) \
RC = 0; \
else if (-1 == gtmioStatus) /* Had legitimate error - return it */ \
RC = errno; \
else \
RC = -1; /* Something kept us from reading what we wanted */ \
}
#define LSEEKWRITE(FDESC, FPTR, FBUFF, FBUFF_LEN, RC) \
{ \
ssize_t gtmioStatus; \
size_t gtmioBuffLen; \
off_t gtmioPtr; \
sm_uc_ptr_t gtmioBuff; \
gtmioBuffLen = FBUFF_LEN; \
gtmioBuff = (sm_uc_ptr_t)(FBUFF); \
gtmioPtr = (off_t)(FPTR); \
for (;;) \
{ \
if (-1 != (gtmioStatus = pwrite(FDESC, gtmioBuff, gtmioBuffLen, gtmioPtr))) \
{ \
gtmioBuffLen -= gtmioStatus; \
if (0 == gtmioBuffLen) \
break; \
gtmioBuff += gtmioStatus; \
gtmioPtr += gtmioStatus; \
continue; \
} \
if (EINTR != errno) \
break; \
} \
if (0 == gtmioBuffLen) \
RC = 0; \
else if (-1 == gtmioStatus) /* Had legitimate error - return it */ \
RC = errno; \
else \
RC = -1; /* Something kept us from writing what we wanted */ \
}
#else /* real lseek and read/write - still need to protect against interrupts inbetween calls */
/* Using lseek/read/write path instead of faster pread/pwrite path */
#ifndef __MVS__
#warning "Using lseek/read/write path instead of faster pread/pwrite path"
#endif
#define NOPIO_ONLY(X) X
/* Note array is not initialized but first IO to a given file descriptor will initialize that element */
#define GET_LSEEK_FLAGS_ARRAY \
{ \
GBLREF boolean_t *lseekIoInProgress_flags; \
int4 sc_open_max; \
if ((boolean_t *)0 == lseekIoInProgress_flags) \
{ \
SYSCONF(_SC_OPEN_MAX, sc_open_max); \
lseekIoInProgress_flags = (boolean_t *)malloc(sc_open_max * SIZEOF(boolean_t)); \
} \
}
#define GET_LSEEK_FLAG(FDESC, VAL) \
{ \
GBLREF boolean_t *lseekIoInProgress_flags; \
GET_LSEEK_FLAGS_ARRAY; \
VAL = lseekIoInProgress_flags[(FDESC)]; \
}
#define SET_LSEEK_FLAG(FDESC, VAL) \
{ \
GBLREF boolean_t *lseekIoInProgress_flags; \
GET_LSEEK_FLAGS_ARRAY; \
lseekIoInProgress_flags[(FDESC)] = VAL; \
}
#define LSEEKREAD(FDESC, FPTR, FBUFF, FBUFF_LEN, RC) \
{ \
GBLREF boolean_t *lseekIoInProgress_flags; \
ssize_t gtmioStatus; \
size_t gtmioBuffLen; \
off_t gtmioPtr; \
sm_uc_ptr_t gtmioBuff; \
SET_LSEEK_FLAG(FDESC, TRUE); \
gtmioBuffLen = FBUFF_LEN; \
gtmioBuff = (sm_uc_ptr_t)(FBUFF); \
gtmioPtr = (off_t)(FPTR); \
for (;;) \
{ \
if (-1 != (gtmioStatus = (ssize_t)lseek(FDESC, gtmioPtr, SEEK_SET))) \
{ \
if (-1 != (gtmioStatus = read(FDESC, gtmioBuff, gtmioBuffLen))) \
{ \
gtmioBuffLen -= gtmioStatus; \
if (0 == gtmioBuffLen || 0 == gtmioStatus) \
break; \
gtmioBuff += gtmioStatus; \
gtmioPtr += gtmioStatus; \
continue; \
} \
} \
if (EINTR != errno) \
break; \
} \
if (0 == gtmioBuffLen) \
RC = 0; \
else if (-1 == gtmioStatus) /* Had legitimate error - return it */ \
RC = errno; \
else \
RC = -1; /* Something kept us from reading what we wanted */ \
SET_LSEEK_FLAG(FDESC, FALSE); /* Reason this is last is so max optimization occurs */ \
}
/* The below macro is almost the same as LSEEKREAD except it has an extra parameter where the number of
* bytes ACTUALLY READ are stored irrespective of whether all REQUESTED BYTES were read or not.
*/
#define LSEEKREAD_AVAILABLE(FDESC, FPTR, FBUFF, FBUFF_LEN, ACTUAL_READLEN, RC) \
{ \
GBLREF boolean_t *lseekIoInProgress_flags; \
ssize_t gtmioStatus; \
size_t gtmioBuffLen; \
off_t gtmioPtr; \
sm_uc_ptr_t gtmioBuff; \
\
SET_LSEEK_FLAG(FDESC, TRUE); \
gtmioBuffLen = FBUFF_LEN; \
gtmioBuff = (sm_uc_ptr_t)(FBUFF); \
gtmioPtr = (off_t)(FPTR); \
for (;;) \
{ \
if (-1 != (gtmioStatus = (ssize_t)lseek(FDESC, gtmioPtr, SEEK_SET))) \
{ \
if (-1 != (gtmioStatus = read(FDESC, gtmioBuff, gtmioBuffLen))) \
{ \
gtmioBuffLen -= gtmioStatus; \
if (0 == gtmioBuffLen || 0 == gtmioStatus) \
break; \
gtmioBuff += gtmioStatus; \
gtmioPtr += gtmioStatus; \
continue; \
} \
} \
if (EINTR != errno) \
break; \
} \
(ACTUAL_READLEN) = (FBUFF_LEN) - gtmioBuffLen; \
if (0 == gtmioBuffLen) \
RC = 0; \
else if (-1 == gtmioStatus) /* Had legitimate error - return it */ \
RC = errno; \
else \
RC = -1; /* Something kept us from reading what we wanted */ \
SET_LSEEK_FLAG(FDESC, FALSE); /* Reason this is last is so max optimization occurs */ \
}
#define LSEEKWRITE(FDESC, FPTR, FBUFF, FBUFF_LEN, RC) \
{ \
GBLREF boolean_t *lseekIoInProgress_flags; \
ssize_t gtmioStatus; \
size_t gtmioBuffLen; \
off_t gtmioPtr; \
sm_uc_ptr_t gtmioBuff; \
SET_LSEEK_FLAG(FDESC, TRUE); \
gtmioBuffLen = FBUFF_LEN; \
gtmioBuff = (sm_uc_ptr_t)(FBUFF); \
gtmioPtr = (off_t)(FPTR); \
for (;;) \
{ \
if (-1 != (gtmioStatus = (ssize_t)lseek(FDESC, gtmioPtr, SEEK_SET))) \
{ \
if (-1 != (gtmioStatus = write(FDESC, gtmioBuff, gtmioBuffLen))) \
{ \
gtmioBuffLen -= gtmioStatus; \
if (0 == gtmioBuffLen) \
break; \
gtmioBuff += gtmioStatus; \
gtmioPtr += gtmioStatus; \
continue; \
} \
} \
if (EINTR != errno) \
break; \
} \
if (0 == gtmioBuffLen) \
RC = 0; \
else if (-1 == gtmioStatus) /* Had legitimate error - return it */ \
RC = errno; \
else \
RC = -1; /* Something kept us from writing what we wanted */ \
SET_LSEEK_FLAG(FDESC, FALSE); /* Reason this is last is so max optimization occurs */ \
}
#endif /* if old lseekread/writes */
#define DOREADRC(FDESC, FBUFF, FBUFF_LEN, RC) \
{ \
ssize_t gtmioStatus; \
size_t gtmioBuffLen; \
sm_uc_ptr_t gtmioBuff; \
gtmioBuffLen = FBUFF_LEN; \
gtmioBuff = (sm_uc_ptr_t)(FBUFF); \
for (;;) \
{ \
if (-1 != (gtmioStatus = read(FDESC, gtmioBuff, gtmioBuffLen))) \
{ \
gtmioBuffLen -= gtmioStatus; \
if (0 == gtmioBuffLen || 0 == gtmioStatus) \
break; \
gtmioBuff += gtmioStatus; \
} \
else if (EINTR != errno) \
break; \
} \
if (-1 == gtmioStatus) /* Had legitimate error - return it */ \
RC = errno; \
else if (0 == gtmioBuffLen) \
RC = 0; \
else \
RC = -1; /* Something kept us from reading what we wanted */ \
}
#define DOREADRL(FDESC, FBUFF, FBUFF_LEN, RLEN) \
{ \
ssize_t gtmioStatus; \
size_t gtmioBuffLen; \
sm_uc_ptr_t gtmioBuff; \
gtmioBuffLen = FBUFF_LEN; \
gtmioBuff = (sm_uc_ptr_t)(FBUFF); \
for (;;) \
{ \
if (-1 != (gtmioStatus = read(FDESC, gtmioBuff, gtmioBuffLen))) \
{ \
gtmioBuffLen -= gtmioStatus; \
if (0 == gtmioBuffLen || 0 == gtmioStatus) \
break; \
gtmioBuff += gtmioStatus; \
} \
else if (EINTR != errno) \
break; \
} \
if (-1 != gtmioStatus) \
RLEN = (int)(FBUFF_LEN - gtmioBuffLen); /* Return length actually read */ \
else /* Had legitimate error - return it */ \
RLEN = -1; \
}
#define DOREADRLTO2(FDESC, FBUFF, FBUFF_LEN, TOFLAG, BLOCKED_IN, ISPIPE, FLAGS, RLEN, \
TOT_BYTES_READ, TIMER_ID, MSEC_TIMEOUT, PIPE_ZERO_TIMEOUT, UTF_VAR_PF, PIPE_OR_FIFO) \
{ \
ssize_t gtmioStatus; \
int skip_read = FALSE; \
int tfcntl_res; \
size_t gtmioBuffLen; \
sm_uc_ptr_t gtmioBuff; \
gtmioBuffLen = (size_t)FBUFF_LEN; \
gtmioBuff = (sm_uc_ptr_t)(FBUFF); \
for (;;) \
{ \
/* if it is a read x:0 on a pipe and it is not blocked (always the case when starting a read) \
then try and read one char. If it succeeds then turn on blocked io and read the rest \
of the line.*/ \
if (ISPIPE && (FALSE == *BLOCKED_IN)) \
{ \
for (;;) \
{ \
if (-1 != (gtmioStatus = read(FDESC, gtmioBuff, 1))) \
{ \
if (0 == gtmioStatus) /* end of file */ \
{ \
skip_read = TRUE; \
break; \
} \
FCNTL3(FDESC, F_SETFL, FLAGS, tfcntl_res); \
if (0 > tfcntl_res) \
rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("fcntl"), CALLFROM, errno); \
*BLOCKED_IN = TRUE; \
if (PIPE_ZERO_TIMEOUT) \
{ \
TOFLAG = FALSE; \
/* Set a timer for 1 sec so atomic read x:0 will still work on \
loaded systems but timeout on incomplete reads. Any characters \
read to this point will be returned. */ \
*MSEC_TIMEOUT = timeout2msec(1); \
start_timer(TIMER_ID, *MSEC_TIMEOUT, wake_alarm, 0, NULL); \
} \
gtmioBuffLen -= gtmioStatus; \
gtmioBuff += gtmioStatus; \
/* if only asked to read 1 character then skip additional read */ \
if (0 == gtmioBuffLen) \
skip_read = TRUE; \
break; \
} else if (EINTR != errno || TOFLAG) \
{ \
skip_read = TRUE; \
break; \
} \
} \
} \
/* if we didn't read 1 character or it's an error don't read anymore now */ \
if (TRUE == skip_read) break; \
if (-1 != (gtmioStatus = read(FDESC, gtmioBuff, gtmioBuffLen))) \
{ \
gtmioBuffLen -= gtmioStatus; \
if (0 == gtmioBuffLen || 0 == gtmioStatus) \
break; \
gtmioBuff += gtmioStatus; \
/* If it is pipe or fifo, read data that is currently available. If pipe contains data less than \
the CHUNK_SIZE, no need to read once again since it is in BLOCKING mode, in which case it will return -1. \
So in the first read itself (after a successful read) break from the infinite loop. This variable is TRUE \
if DOREADRLTO2 macro is called for a CHUNK_SIZE read. In other places (eg: iorm_get) it will be FALSE. \
*/ \
if (UTF_VAR_PF) \
break; \
} else if (EINTR != errno || TOFLAG) \
break; \
if (PIPE_OR_FIFO && outofband) \
break; \
} \
if (-1 != gtmioStatus) \
RLEN = (int)(FBUFF_LEN - gtmioBuffLen); /* Return length actually read */ \
else /* Had legitimate error - return it */ \
{ \
/* Store the number of bytes read in this invocation before we error out */ \
*TOT_BYTES_READ = (int)(FBUFF_LEN - gtmioBuffLen); \
RLEN = -1; \
} \
}
#define DOWRITE(FDESC, FBUFF, FBUFF_LEN) \
{ \
ssize_t gtmioStatus; \
size_t gtmioBuffLen; \
sm_uc_ptr_t gtmioBuff; \
gtmioBuffLen = FBUFF_LEN; \
gtmioBuff = (sm_uc_ptr_t)(FBUFF); \
assert(0 != gtmioBuffLen); \
for (;;) \
{ \
if (-1 != (gtmioStatus = write(FDESC, gtmioBuff, gtmioBuffLen))) \
{ \
gtmioBuffLen -= gtmioStatus; \
if (0 == gtmioBuffLen) \
break; \
gtmioBuff += gtmioStatus; \
} \
else if (EINTR != errno) \
break; \
} \
/* GTMASSERT? */ \
}
#define DOWRITERC(FDESC, FBUFF, FBUFF_LEN, RC) \
{ \
GBLREF int gtm_non_blocked_write_retries; \
ssize_t gtmioStatus; \
size_t gtmioBuffLen; \
sm_uc_ptr_t gtmioBuff; \
int block_cnt = 0; \
gtmioBuffLen = FBUFF_LEN; \
gtmioBuff = (sm_uc_ptr_t)(FBUFF); \
for (;;) \
{ \
if (-1 != (gtmioStatus = write(FDESC, gtmioBuff, gtmioBuffLen))) \
{ \
gtmioBuffLen -= gtmioStatus; \
if (0 == gtmioBuffLen) \
break; \
gtmioBuff += gtmioStatus; \
} \
else if (EINTR != errno && EAGAIN != errno) \
break; \
else if (EAGAIN == errno) \
{ \
if (gtm_non_blocked_write_retries <= block_cnt) \
break; \
SHORT_SLEEP(WAIT_FOR_BLOCK_TIME); \
block_cnt++; \
} \
} \
if (-1 == gtmioStatus) /* Had legitimate error - return it */ \
RC = errno; \
else if (0 == gtmioBuffLen) \
RC = 0; \
else \
RC = -1; /* Something kept us from writing what we wanted */ \
}
#define DOLLAR_DEVICE_SET(DEVPTR,STATUS) \
{ \
len = SIZEOF(ONE_COMMA) - 1; \
memcpy(DEVPTR->dollar_device, ONE_COMMA, len); \
errptr = (char *)STRERROR(STATUS); \
/* make sure there is room for the 1, and the null at the end */ \
errlen = MIN(STRLEN(errptr), SIZEOF(DEVPTR->dollar_device) - SIZEOF(ONE_COMMA)); \
memcpy(&DEVPTR->dollar_device[len], errptr, errlen); \
DEVPTR->dollar_device[len + errlen] = '\0'; \
}
#define DOLLAR_DEVICE_WRITE(DEVPTR,STATUS) \
{ \
int len; \
int errlen; \
char *errptr; \
/* save error in $device */ \
if (EAGAIN == STATUS) \
{ \
len = SIZEOF(ONE_COMMA_UNAVAILABLE); \
memcpy(DEVPTR->dollar_device, ONE_COMMA_UNAVAILABLE, len); \
} else \
DOLLAR_DEVICE_SET(DEVPTR,STATUS); \
}
#define DOWRITERL(FDESC, FBUFF, FBUFF_LEN, RLEN) \
{ \
ssize_t gtmioStatus; \
size_t gtmioBuffLen; \
sm_uc_ptr_t gtmioBuff; \
int block_cnt = 0; \
\
GBLREF int gtm_non_blocked_write_retries; \
\
gtmioBuffLen = FBUFF_LEN; \
gtmioBuff = (sm_uc_ptr_t)(FBUFF); \
for (;;) \
{ \
if (-1 != (gtmioStatus = write(FDESC, gtmioBuff, gtmioBuffLen))) \
{ \
gtmioBuffLen -= gtmioStatus; \
if (0 == gtmioBuffLen) \
break; \
gtmioBuff += gtmioStatus; \
} \
else if (EINTR != errno && EAGAIN != errno) \
break; \
else if (EAGAIN == errno) \
{ \
if (gtm_non_blocked_write_retries <= block_cnt) \
break; \
SHORT_SLEEP(WAIT_FOR_BLOCK_TIME); \
block_cnt++; \
} \
} \
if (-1 != gtmioStatus) \
RLEN = (int)(FBUFF_LEN - gtmioBuffLen); /* Return length actually written */ \
else /* Had legitimate error - return it */ \
RLEN = -1; \
}
#define DO_FILE_READ(CHANNEL, OFFSET, READBUFF, LEN, STATUS1, STATUS2) \
{ \
error_def(ERR_PREMATEOF); \
\
STATUS2 = SS_NORMAL; \
LSEEKREAD(CHANNEL, OFFSET, READBUFF, LEN, STATUS1); \
if (-1 == STATUS1) \
STATUS1 = ERR_PREMATEOF; \
}
#define DO_FILE_WRITE(CHANNEL, OFFSET, WRITEBUFF, LEN, STATUS1, STATUS2) \
{ \
error_def(ERR_PREMATEOF); \
\
STATUS2 = SS_NORMAL; \
LSEEKWRITE(CHANNEL, OFFSET, WRITEBUFF, LEN, STATUS1); \
if (-1 == STATUS1) \
STATUS1 = ERR_PREMATEOF; \
}
typedef struct
{
int fd;
mstr v;
} file_pointer;
/* WRITEPIPE is a work-around for a problem found in z/OS where the kernel doesn't seem to
* break up writes into small enough pieces, requiring that we do it ourselves. The fix is
* applied to all Unix platforms, even though all except z/OS seem to work with monster writes.
*/
#define WRITEPIPE(FDESC, PIPESZ, FBUFF, FBUFF_LEN, RC) \
{ \
GBLREF int gtm_non_blocked_write_retries; \
ssize_t gtmioStatus; \
size_t gtmioBuffLen; \
size_t shortBuffLen; \
sm_uc_ptr_t gtmioBuff; \
int block_cnt = 0; \
gtmioBuffLen = FBUFF_LEN; \
gtmioBuff = (sm_uc_ptr_t)(FBUFF); \
for (;;) \
{ \
shortBuffLen = MIN(PIPESZ, gtmioBuffLen); \
if (-1 != (gtmioStatus = write(FDESC, gtmioBuff, shortBuffLen))) \
{ \
gtmioBuffLen -= gtmioStatus; \
if (0 == gtmioBuffLen) \
break; \
gtmioBuff += gtmioStatus; \
} \
else if (EINTR != errno && EAGAIN != errno) \
break; \
else if (EAGAIN == errno) \
{ \
if (gtm_non_blocked_write_retries <= block_cnt) \
break; \
SHORT_SLEEP(WAIT_FOR_BLOCK_TIME); \
block_cnt++; \
} \
} \
if (-1 == gtmioStatus) /* Had legitimate error - return it */ \
RC = errno; \
else if (0 == gtmioBuffLen) \
RC = 0; \
else \
RC = -1; /* Something kept us from writing what we wanted */ \
}
#define FFLUSH(STREAM) \
{ \
DEFER_INTERRUPTS(INTRPT_IN_FFLUSH); \
fflush(STREAM); \
ENABLE_INTERRUPTS(INTRPT_IN_FFLUSH); \
}
#endif