mirror of
https://github.com/SDL-Hercules-390/hyperion.git
synced 2026-04-09 22:01:57 +02:00
. Fix compiler warnings on macOS. . Fix compiler warnings on GCC 12. . Use performance cores on Apple Silicon. (Enrico Sorichetti) . Save maxprio/minprio in sysblk so impl() doesn't clobber them. (Tyler Mitchell) . Handle non-GNU Linuxes properly. (Tyler Mitchell) . getprotobyname: some Linuxes want "tcp" not "TCP". (Tyler Mitchell) . Fix for occasional crash on exit. (Tyler Mitchell): In POSIX, detach_thread() is neither needed nor valid after a return from join_thread(). When the latter returns, the thread has exited and its resources are released, so detach_thread() not only is not needed, but references deallocated resources. This can be harmless, but not always.
1406 lines
45 KiB
C
1406 lines
45 KiB
C
/* SCRIPT.C (C) Copyright Roger Bowler, 1999-2012 */
|
|
/* (C) Copyright Jan Jaeger, 1999-2012 */
|
|
/* (C) Copyright Ivan Warren, 2003-2012 */
|
|
/* (C) Copyright "Fish" (David B. Trout), 2002-2012 */
|
|
/* (C) Copyright TurboHercules, SAS 2010-2011 */
|
|
/* ESA/390 Configuration and Script parser */
|
|
/* */
|
|
/* Released under "The Q Public License Version 1" */
|
|
/* (http://www.hercules-390.org/herclic.html) as modifications to */
|
|
/* Hercules. */
|
|
|
|
/* This module contains all hercules specific config and command */
|
|
/* scripting routines. */
|
|
|
|
/* All hercules specific language contructs, keywords and semantics */
|
|
/* are defined in this modeule */
|
|
|
|
#include "hstdinc.h"
|
|
|
|
#define _SCRIPT_C_
|
|
#define _HENGINE_DLL_
|
|
|
|
#include "hercules.h"
|
|
#include "devtype.h"
|
|
#include "opcode.h"
|
|
#include "hostinfo.h"
|
|
|
|
#if !defined(_GEN_ARCH)
|
|
|
|
#if defined(_ARCH_NUM_2)
|
|
#define _GEN_ARCH _ARCH_NUM_2
|
|
#include "script.c"
|
|
#undef _GEN_ARCH
|
|
#endif
|
|
|
|
#if defined(_ARCH_NUM_1)
|
|
#define _GEN_ARCH _ARCH_NUM_1
|
|
#include "script.c"
|
|
#undef _GEN_ARCH
|
|
#endif
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Script processing control */
|
|
/*-------------------------------------------------------------------*/
|
|
struct SCRCTL { /* Script control structure */
|
|
LIST_ENTRY link; /* Just a link in the chain */
|
|
TID scr_tid; /* Script thread id. If zero
|
|
then entry is not active. */
|
|
int scr_id; /* Script identification no. */
|
|
char* scr_name; /* Name of script being run */
|
|
char* scr_cmdline; /* Original command-line */
|
|
int scr_recursion; /* Current recursion level */
|
|
int scr_flags; /* Script processing flags */
|
|
#define SCR_CANCEL 0x80 /* Cancel script requested */
|
|
#define SCR_ABORT 0x40 /* Script abort requested */
|
|
#define SCR_CANCELED 0x20 /* Script has been canceled */
|
|
#define SCR_ABORTED 0x10 /* Script has been aborted */
|
|
};
|
|
typedef struct SCRCTL SCRCTL; /* typedef is easier to use */
|
|
static LIST_ENTRY scrlist = {0,0}; /* Script list anchor entry */
|
|
static int scrid = 0; /* Script identification no. */
|
|
|
|
/* Forward declarations: */
|
|
static int do_special(char *fname, int *inc_stmtnum, SCRCTL *pCtl, char *p);
|
|
static int set_restart(const char * s);
|
|
/* End of forward declarations. */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Subroutine to read a statement from the configuration file */
|
|
/* addargc Contains number of arguments */
|
|
/* addargv An array of pointers to each argument */
|
|
/* Returns 0 if successful, -1 if end of file */
|
|
/*-------------------------------------------------------------------*/
|
|
static int read_config (char *fname, FILE *fp, char *buf, unsigned int buflen, int *inc_stmtnum)
|
|
{
|
|
int c; /* Character work area */
|
|
unsigned int stmtlen; /* Statement length */
|
|
int lstarted; /* Indicate if non-whitespace*/
|
|
/* has been seen yet in line */
|
|
char *buf1; /* Pointer to resolved buffer*/
|
|
|
|
while (1)
|
|
{
|
|
/* Increment statement number */
|
|
(*inc_stmtnum)++;
|
|
|
|
/* Read next statement from configuration file */
|
|
for (stmtlen = 0, lstarted = 0; ;)
|
|
{
|
|
if (stmtlen == 0)
|
|
memset(buf, 0, buflen); // clear work area
|
|
|
|
/* Read character from configuration file */
|
|
c = fgetc(fp);
|
|
|
|
/* Check for I/O error */
|
|
if (ferror(fp))
|
|
{
|
|
WRMSG(HHC01432, "S", *inc_stmtnum, fname, "fgetc()", strerror(errno));
|
|
return -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 >= buflen - 1)
|
|
{
|
|
WRMSG(HHC01433, "S", *inc_stmtnum, fname);
|
|
return -1;
|
|
}
|
|
|
|
/* Append character to buffer */
|
|
buf[stmtlen++] = c;
|
|
|
|
} /* end for(stmtlen) */
|
|
|
|
/* Null terminate the buffer */
|
|
buf[ stmtlen ] = 0;
|
|
|
|
/* Remove trailing whitespace */
|
|
RTRIM( buf );
|
|
stmtlen = (int) strlen( buf );
|
|
|
|
set_symbol("CUU","$(CUU)");
|
|
set_symbol("CCUU","$(CCUU)");
|
|
set_symbol("DEVN","$(DEVN)");
|
|
|
|
/* Perform variable substitution */
|
|
buf1=resolve_symbol_string(buf);
|
|
|
|
if(buf1!=NULL)
|
|
{
|
|
if(strlen(buf1)>=buflen)
|
|
{
|
|
WRMSG(HHC01433, "S", *inc_stmtnum, fname);
|
|
free(buf1);
|
|
return -1;
|
|
}
|
|
strlcpy(buf,buf1,buflen);
|
|
free(buf1);
|
|
stmtlen = strlen( buf );
|
|
}
|
|
|
|
/* Loud comments should always be logged */
|
|
if (stmtlen != 0 && buf[0] == '*')
|
|
WRMSG( HHC01603, "I", buf ); // "%s"
|
|
|
|
/* Ignore null statements and comments */
|
|
if (stmtlen == 0 || buf[0] == '*' || buf[0] == '#')
|
|
continue;
|
|
|
|
/* Special handling for 'pause' and other statements */
|
|
if (do_special(fname, inc_stmtnum, NULL, buf))
|
|
continue;
|
|
|
|
break;
|
|
} /* end while */
|
|
|
|
return 0;
|
|
} /* end function read_config */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Function to build system configuration */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT int process_config (const char *cfg_name)
|
|
{
|
|
char buf[ MAX_CFG_LINELEN ]; /* Config statement buffer */
|
|
int addargc; /* Number of additional args */
|
|
char *addargv[MAX_ARGS]; /* Additional argument array */
|
|
|
|
#define MAX_INC_LEVEL 8 /* Maximum nest level */
|
|
static int inc_stmtnum[MAX_INC_LEVEL]; /* statement number */
|
|
|
|
int rc; /* Return code */
|
|
int i; /* Array subscript */
|
|
int scount; /* Statement counter */
|
|
int inc_level; /* Current nesting level */
|
|
FILE *inc_fp[MAX_INC_LEVEL]; /* Configuration file pointer*/
|
|
BYTE c; /* Work area for sscanf */
|
|
|
|
int inc_ignore_errors = 0; /* 1==ignore include errors */
|
|
char pathname[MAX_PATH]; /* file path in host format */
|
|
|
|
char fname[MAX_PATH]; /* normalized filename */
|
|
int errorcount = 0;
|
|
|
|
#if defined(HAVE_OBJECT_REXX) || defined(HAVE_REGINA_REXX)
|
|
int shell_flg = FALSE; /* indicate it is has a shell
|
|
path specified */
|
|
#endif
|
|
|
|
/* Open the base configuration file */
|
|
hostpath(fname, cfg_name, sizeof(fname));
|
|
|
|
inc_level = 0;
|
|
#if defined(_MSVC_)
|
|
fopen_s( &inc_fp[inc_level], fname, "r");
|
|
#else
|
|
inc_fp[inc_level] = fopen (fname, "r");
|
|
#endif
|
|
if (inc_fp[inc_level] == NULL)
|
|
{
|
|
WRMSG(HHC01432, "S", 1, fname, "fopen()", strerror(errno));
|
|
fflush(stderr);
|
|
fflush(stdout);
|
|
USLEEP(100000);
|
|
return -1;
|
|
}
|
|
|
|
inc_stmtnum[inc_level] = 0;
|
|
|
|
/*****************************************************************/
|
|
/* 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], buf, sizeof(buf), &inc_stmtnum[inc_level]))
|
|
{
|
|
fclose (inc_fp[inc_level--]);
|
|
}
|
|
if (inc_level < 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* Parse the statement just read */
|
|
parse_args (buf, MAX_ARGS, addargv, &addargc);
|
|
|
|
#if defined(HAVE_OBJECT_REXX) || defined(HAVE_REGINA_REXX)
|
|
// Test for REXX script. If first card starts with "/*" or
|
|
// first card has shell path specification "#!" and second card
|
|
// "/*", then we will process as a REXX script.
|
|
|
|
if ( inc_level == 0 && inc_stmtnum[0] < 3 )
|
|
{
|
|
/* Ignore #! (shell path) card if found */
|
|
if ( shell_flg == FALSE &&
|
|
inc_stmtnum[0] == 1 &&
|
|
!strncmp( addargv[0], "#!", 2 ) )
|
|
{
|
|
shell_flg = TRUE;
|
|
}
|
|
/* Check for REXX exec being executed */
|
|
if ( !strncmp( addargv[0], "/*", 2 ) &&
|
|
( ( shell_flg == FALSE && inc_stmtnum[0] == 1 ) ||
|
|
( shell_flg == TRUE && inc_stmtnum[0] == 2 )
|
|
)
|
|
)
|
|
{
|
|
char *rcmd[2] = { "exec", NULL };
|
|
rcmd[1] = fname;
|
|
errorcount = exec_cmd( 2, rcmd, NULL );
|
|
goto rexx_done;
|
|
}
|
|
}
|
|
#endif /* defined(HAVE_OBJECT_REXX) || defined(HAVE_REGINA_REXX) */
|
|
|
|
if (strcasecmp (addargv[0], "ignore") == 0)
|
|
{
|
|
if (strcasecmp (addargv[1], "include_errors") == 0)
|
|
{
|
|
WRMSG(HHC01435, "I", fname);
|
|
inc_ignore_errors = 1 ;
|
|
}
|
|
|
|
continue ;
|
|
}
|
|
|
|
/* Check for include statement */
|
|
if (strcasecmp (addargv[0], "include") == 0)
|
|
{
|
|
if (++inc_level >= MAX_INC_LEVEL)
|
|
{
|
|
WRMSG(HHC01436, "S", inc_stmtnum[inc_level-1], fname, MAX_INC_LEVEL);
|
|
return -1;
|
|
}
|
|
|
|
hostpath(pathname, addargv[1], sizeof(pathname));
|
|
WRMSG(HHC01437, "I", inc_stmtnum[inc_level-1], fname, pathname);
|
|
#if defined(_MSVC_)
|
|
fopen_s ( &inc_fp[inc_level], pathname, "r");
|
|
#else
|
|
inc_fp[inc_level] = fopen (pathname, "r");
|
|
#endif
|
|
if (inc_fp[inc_level] == NULL)
|
|
{
|
|
inc_level--;
|
|
if ( inc_ignore_errors == 1 )
|
|
{
|
|
WRMSG(HHC01438, "W", fname, addargv[1], strerror(errno));
|
|
continue ;
|
|
}
|
|
else
|
|
{
|
|
WRMSG(HHC01439, "S", fname, addargv[1], strerror(errno));
|
|
return -1;
|
|
}
|
|
}
|
|
inc_stmtnum[inc_level] = 0;
|
|
continue;
|
|
}
|
|
|
|
if ( ( strlen(addargv[0]) <= 4 &&
|
|
sscanf(addargv[0], "%"SCNx32"%c", &rc, &c) == 1 )
|
|
||
|
|
/* Also, if addargv[0] contains ':' (added by Harold Grovesteen jan2008) */
|
|
/* Added because device statements may now contain channel set or LCSS id */
|
|
strchr( addargv[0], ':' )
|
|
/* ISW */
|
|
/* Also, if addargv[0] contains '-', ',' or '.' */
|
|
/* Added because device statements may now be a compound device number specification */
|
|
||
|
|
strchr( addargv[0],'-' )
|
|
||
|
|
strchr( addargv[0],'.' )
|
|
||
|
|
strchr( addargv[0],',' )
|
|
) /* end if */
|
|
{
|
|
#define MAX_CMD_LEN 32768
|
|
int attargc;
|
|
char **attargv;
|
|
char attcmdline[MAX_CMD_LEN];
|
|
|
|
if ( addargv[0] == NULL || addargv[1] == NULL )
|
|
{
|
|
WRMSG(HHC01448, "S", inc_stmtnum[inc_level], fname);
|
|
return -1;
|
|
}
|
|
|
|
/* Build attach command to attach device(s) */
|
|
attargc = addargc + 1;
|
|
attargv = malloc( attargc * sizeof(char *) );
|
|
|
|
attargv[0] = "attach";
|
|
STRLCPY( attcmdline, attargv[0] );
|
|
for ( i = 1; i < attargc; i++ )
|
|
{
|
|
attargv[i] = addargv[i - 1];
|
|
STRLCAT( attcmdline, " " );
|
|
STRLCAT( attcmdline, attargv[i] );
|
|
}
|
|
|
|
rc = CallHercCmd( attargc, attargv, attcmdline );
|
|
|
|
free( attargv );
|
|
|
|
if ( rc == -2 )
|
|
{
|
|
WRMSG(HHC01443, "S", inc_stmtnum[inc_level], fname, addargv[0], "device number specification");
|
|
return -1;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
/* Check for old-style CPU statement */
|
|
if (scount == 0 && addargc == 7 && strlen(addargv[0]) == 6
|
|
&& sscanf(addargv[0], "%"SCNx32"%c", &rc, &c) == 1)
|
|
{
|
|
char *exec_cpuserial[2] = { "cpuserial", NULL };
|
|
char *exec_cpumodel[2] = { "cpumodel", NULL };
|
|
char *exec_mainsize[2] = { "mainsize", NULL };
|
|
char *exec_xpndsize[2] = { "xpndsize", NULL };
|
|
char *exec_cnslport[2] = { "cnslport", NULL };
|
|
char *exec_numcpu[2] = { "numcpu", NULL };
|
|
char *exec_loadparm[2] = { "loadparm", NULL };
|
|
|
|
exec_cpuserial[1] = addargv[0];
|
|
exec_cpumodel[1] = addargv[1];
|
|
exec_mainsize[1] = addargv[2];
|
|
exec_xpndsize[1] = addargv[3];
|
|
exec_cnslport[1] = addargv[4];
|
|
exec_numcpu[1] = addargv[5];
|
|
exec_loadparm[1] = addargv[6];
|
|
|
|
if(CallHercCmd (2, exec_cpuserial, NULL) < 0 )
|
|
errorcount++;
|
|
if(CallHercCmd (2, exec_cpumodel, NULL) < 0 )
|
|
errorcount++;
|
|
if(CallHercCmd (2, exec_mainsize, NULL) < 0 )
|
|
errorcount++;
|
|
if(CallHercCmd (2, exec_xpndsize, NULL) < 0 )
|
|
errorcount++;
|
|
if(CallHercCmd (2, exec_cnslport, NULL) < 0 )
|
|
errorcount++;
|
|
if(CallHercCmd (2, exec_numcpu, NULL) < 0 )
|
|
errorcount++;
|
|
if(CallHercCmd (2, exec_loadparm, NULL) < 0 )
|
|
errorcount++;
|
|
|
|
if(errorcount)
|
|
WRMSG(HHC01441, "E", inc_stmtnum[inc_level], fname, addargv[0]);
|
|
}
|
|
else
|
|
{
|
|
char addcmdline[MAX_CMD_LEN];
|
|
int i;
|
|
int rc;
|
|
|
|
STRLCPY( addcmdline, addargv[0] );
|
|
for( i = 1; i < addargc; i++ )
|
|
{
|
|
STRLCAT( addcmdline, " " );
|
|
STRLCAT( addcmdline, addargv[i] );
|
|
}
|
|
|
|
rc = CallHercCmd (addargc, addargv, addcmdline);
|
|
|
|
/* rc < 0 abort, rc == 0 OK, rc > warnings */
|
|
|
|
if( rc < 0 )
|
|
{
|
|
errorcount++;
|
|
WRMSG(HHC01441, "E", inc_stmtnum[inc_level], fname, addcmdline);
|
|
}
|
|
|
|
} /* end else (not old-style CPU statement) */
|
|
|
|
} /* end for(scount) (end of configuration file statement loop) */
|
|
|
|
#if defined(HAVE_OBJECT_REXX) || defined(HAVE_REGINA_REXX)
|
|
|
|
rexx_done:
|
|
|
|
if(!sysblk.msglvl)
|
|
sysblk.msglvl = DEFAULT_MLVL;
|
|
|
|
return errorcount;
|
|
|
|
#endif // defined(HAVE_OBJECT_REXX) || defined(HAVE_REGINA_REXX)
|
|
|
|
} /* end function process_config */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Create new SCRCTL entry - lock must *NOT* be held (internal) */
|
|
/*-------------------------------------------------------------------*/
|
|
static
|
|
SCRCTL* NewSCRCTL( TID tid, const char* script_name )
|
|
{
|
|
SCRCTL* pCtl;
|
|
char* scr_name;
|
|
ASSERT( script_name );
|
|
|
|
/* Create a new entry */
|
|
if (0
|
|
|| !(pCtl = malloc (sizeof( SCRCTL )))
|
|
|| !(scr_name = strdup( script_name ))
|
|
)
|
|
{
|
|
// "Out of memory"
|
|
WRMSG( HHC00152, "E" );
|
|
if (pCtl) free( pCtl );
|
|
return NULL;
|
|
}
|
|
|
|
/* Initialize the new entry */
|
|
memset( pCtl, 0, sizeof( SCRCTL ));
|
|
InitializeListLink( &pCtl->link );
|
|
pCtl->scr_tid = tid; /* (may be zero) */
|
|
pCtl->scr_name = scr_name;
|
|
|
|
/* Add the new entry to our list */
|
|
obtain_lock( &sysblk.scrlock );
|
|
pCtl->scr_id = ++scrid;
|
|
if (!scrlist.Flink)
|
|
InitializeListHead( &scrlist );
|
|
InsertListTail( &scrlist, &pCtl->link );
|
|
release_lock( &sysblk.scrlock );
|
|
|
|
return pCtl;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Update SCRCTL entry - lock must *NOT* be held (internal) */
|
|
/*-------------------------------------------------------------------*/
|
|
void UpdSCRCTL( SCRCTL* pCtl, const char* script_name )
|
|
{
|
|
obtain_lock( &sysblk.scrlock );
|
|
|
|
if (pCtl->scr_name) free( pCtl->scr_name );
|
|
pCtl->scr_name = strdup( script_name );
|
|
|
|
release_lock( &sysblk.scrlock );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Free SCRCTL entry - lock must *NOT* be held (internal) */
|
|
/*-------------------------------------------------------------------*/
|
|
void FreeSCRCTL( SCRCTL* pCtl )
|
|
{
|
|
ASSERT( pCtl ); /* (sanity check) */
|
|
|
|
/* Remove ENTRY from processing list */
|
|
obtain_lock( &sysblk.scrlock );
|
|
RemoveListEntry( &pCtl->link );
|
|
release_lock( &sysblk.scrlock );
|
|
|
|
/* Free list entry and return */
|
|
if (pCtl->scr_name)
|
|
free( pCtl->scr_name );
|
|
if (pCtl->scr_cmdline)
|
|
free( pCtl->scr_cmdline );
|
|
free( pCtl );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Find SCRCTL entry - lock must *NOT* be held (internal, external) */
|
|
/*-------------------------------------------------------------------*/
|
|
void* FindSCRCTL( TID tid )
|
|
{
|
|
/* PROGRAMMING NOTE: the return type is "void*" so external
|
|
callers are able to query whether any scripts are running
|
|
without requiring them to known what our internal SCRCTL
|
|
struct looks like.
|
|
*/
|
|
LIST_ENTRY* pLink = NULL;
|
|
SCRCTL* pCtl = NULL;
|
|
|
|
ASSERT( tid ); /* (sanity check) */
|
|
|
|
obtain_lock( &sysblk.scrlock );
|
|
|
|
/* Perform first-time initialization if needed */
|
|
if (!scrlist.Flink)
|
|
InitializeListHead( &scrlist );
|
|
|
|
/* Search the list to locate the desired entry */
|
|
for (pLink = scrlist.Flink; pLink != &scrlist; pLink = pLink->Flink)
|
|
{
|
|
pCtl = CONTAINING_RECORD( pLink, SCRCTL, link );
|
|
if (pCtl->scr_tid && equal_threads( pCtl->scr_tid, tid ))
|
|
{
|
|
release_lock( &sysblk.scrlock );
|
|
return pCtl; /* (found) */
|
|
}
|
|
}
|
|
|
|
release_lock( &sysblk.scrlock );
|
|
return NULL; /* (not found) */
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* List script ids - lock must *NOT* be held (internal, external) */
|
|
/*-------------------------------------------------------------------*/
|
|
static void ListScriptsIds()
|
|
{
|
|
LIST_ENTRY* pLink = NULL;
|
|
SCRCTL* pCtl = NULL;
|
|
|
|
obtain_lock( &sysblk.scrlock );
|
|
|
|
/* Perform first-time initialization if needed */
|
|
if (!scrlist.Flink)
|
|
InitializeListHead( &scrlist );
|
|
|
|
/* Check for empty list */
|
|
if (IsListEmpty( &scrlist ))
|
|
{
|
|
// "No scripts currently running"
|
|
WRMSG( HHC02314, "I" );
|
|
release_lock( &sysblk.scrlock );
|
|
return;
|
|
}
|
|
|
|
/* Display all entries in list */
|
|
for (pLink = scrlist.Flink; pLink != &scrlist; pLink = pLink->Flink)
|
|
{
|
|
pCtl = CONTAINING_RECORD( pLink, SCRCTL, link );
|
|
if (!pCtl->scr_tid) continue; /* (inactive) */
|
|
// "Script id:%d, tid:"TIDPAT", level:%d, name:%s"
|
|
WRMSG( HHC02315, "I", pCtl->scr_id,
|
|
TID_CAST( pCtl->scr_tid ), pCtl->scr_recursion,
|
|
pCtl->scr_name ? pCtl->scr_name : "" );
|
|
}
|
|
|
|
release_lock( &sysblk.scrlock );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* cscript command - cancel a running script(s) (external) */
|
|
/*-------------------------------------------------------------------*/
|
|
int cscript_cmd( int argc, char *argv[], char *cmdline )
|
|
{
|
|
int first = FALSE; /* Cancel first script found */
|
|
int all = FALSE; /* Cancel all running scripts */
|
|
int scrid = 0; /* Cancel this specific script */
|
|
int count = 0; /* Counts #of scripts canceled */
|
|
LIST_ENTRY* pLink = NULL; /* (work) */
|
|
SCRCTL* pCtl = NULL; /* (work) */
|
|
|
|
UNREFERENCED( cmdline );
|
|
|
|
/* Optional argument is the identity of the script to cancel
|
|
or "*" (or "all") to cancel all running scripts. The default
|
|
if no argument is given is to cancel only the first script.
|
|
*/
|
|
if (argc > 2)
|
|
{
|
|
// "Invalid number of arguments"
|
|
WRMSG( HHC02446, "E" );
|
|
return -1;
|
|
}
|
|
if (argc < 2)
|
|
{
|
|
/* Default to first one found */
|
|
all = FALSE;
|
|
first = TRUE;
|
|
}
|
|
else /* argc == 2 */
|
|
{
|
|
/* Cancel all running scripts? */
|
|
if (0
|
|
|| strcasecmp( argv[1], "*" ) == 0
|
|
|| strcasecmp( argv[1], "all" ) == 0
|
|
)
|
|
{
|
|
all = TRUE;
|
|
first = FALSE;
|
|
}
|
|
else /* Specific script */
|
|
{
|
|
all = FALSE;
|
|
first = FALSE;
|
|
|
|
if ((scrid = atoi( argv[1] )) == 0)
|
|
{
|
|
// "Invalid argument '%s'%s"
|
|
WRMSG( HHC02205, "E", argv[1], ": value not numeric" );
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
obtain_lock( &sysblk.scrlock );
|
|
|
|
if (!scrlist.Flink || IsListEmpty( &scrlist ))
|
|
{
|
|
// "No scripts currently running"
|
|
WRMSG( HHC02314, "E" );
|
|
release_lock( &sysblk.scrlock );
|
|
return -1;
|
|
}
|
|
|
|
/* Search list for the script(s) to cancel... */
|
|
for (pLink = scrlist.Flink; pLink != &scrlist; pLink = pLink->Flink)
|
|
{
|
|
pCtl = CONTAINING_RECORD( pLink, SCRCTL, link );
|
|
if (!pCtl->scr_tid) continue; /* (inactive) */
|
|
|
|
if (first)
|
|
{
|
|
pCtl->scr_flags |= SCR_CANCEL;
|
|
broadcast_condition( &sysblk.scrcond );
|
|
release_lock( &sysblk.scrlock );
|
|
return 0;
|
|
}
|
|
else if (all)
|
|
{
|
|
pCtl->scr_flags |= SCR_CANCEL;
|
|
count++;
|
|
}
|
|
else if (pCtl->scr_id == scrid)
|
|
{
|
|
pCtl->scr_flags |= SCR_CANCEL;
|
|
count++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (count)
|
|
broadcast_condition( &sysblk.scrcond );
|
|
|
|
release_lock( &sysblk.scrlock );
|
|
|
|
if (!count)
|
|
{
|
|
// "Script %s not found"
|
|
WRMSG( HHC02316, "E", argv[1] );
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Check if script has aborted or been canceled (internal) */
|
|
/*-------------------------------------------------------------------*/
|
|
static int script_abort( SCRCTL* pCtl )
|
|
{
|
|
int abort;
|
|
if (pCtl->scr_flags & SCR_CANCEL) /* cancel requested? */
|
|
{
|
|
if (!(pCtl->scr_flags & SCR_CANCELED)) /* no msg given yet? */
|
|
{
|
|
// "Script %d aborted: '%s'"
|
|
WRMSG( HHC02259, "E", pCtl->scr_id, "user cancel request" );
|
|
pCtl->scr_flags |= SCR_CANCELED;
|
|
}
|
|
pCtl->scr_flags |= SCR_ABORT; /* request abort */
|
|
}
|
|
abort = (pCtl->scr_flags & SCR_ABORT) ? 1 : 0;
|
|
return abort;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Process a single script file (internal, external) */
|
|
/*-------------------------------------------------------------------*/
|
|
int process_script_file( const char* script_name, bool isrcfile )
|
|
{
|
|
SCRCTL* pCtl; /* Script processing control */
|
|
char *scrname; /* Resolved script name */
|
|
char script_path[MAX_PATH]; /* Full path of script file */
|
|
FILE *fp = NULL; /* Script FILE pointer */
|
|
char stmt[ MAX_SCRIPT_STMT ]; /* script statement buffer */
|
|
int stmtlen = 0; /* Length of current stmt */
|
|
TID tid = thread_id(); /* Our thread Id */
|
|
char *p; /* (work) */
|
|
int rc; /* (work) */
|
|
|
|
/* Retrieve our control entry */
|
|
if (!(pCtl = FindSCRCTL( tid )))
|
|
{
|
|
/* If not found it's probably the Hercules ".RC" file */
|
|
ASSERT( isrcfile );
|
|
|
|
/* Create a temporary working control entry */
|
|
if (!(pCtl = NewSCRCTL( tid, script_name )))
|
|
return -1; /* (error message already issued) */
|
|
|
|
/* Start over again using our temporary control entry. */
|
|
rc = process_script_file( script_name, isrcfile );
|
|
FreeSCRCTL( pCtl );
|
|
return rc;
|
|
}
|
|
|
|
/* Abort script if already at maximum recursion level */
|
|
if (pCtl->scr_recursion >= MAX_SCRIPT_DEPTH)
|
|
{
|
|
// "Script %d aborted: '%s'"
|
|
WRMSG( HHC02259, "E", pCtl->scr_id, "script recursion level exceeded" );
|
|
pCtl->scr_flags |= SCR_ABORT;
|
|
return -1;
|
|
}
|
|
|
|
if (!(scrname = resolve_symbol_string( script_name )))
|
|
scrname = strdup( script_name );
|
|
|
|
if (!strcmp(scrname, "-")) /* Standard input? */
|
|
{
|
|
fp = stdin;
|
|
strcpy(script_path, "<stdin>");
|
|
}
|
|
else
|
|
{
|
|
/* Open the specified script file */
|
|
hostpath( script_path, scrname, sizeof( script_path ));
|
|
if (!(fp = fopen( script_path, "r" )))
|
|
{
|
|
/* We get here with the default script file only when */
|
|
/* hercules.rc exists as tested in impl.c. Any error */
|
|
/* opening is should be reported to the user. */
|
|
|
|
int save_errno = errno; /* (save error code for caller) */
|
|
|
|
if (ENOENT != errno) /* If NOT "File Not found" */
|
|
{
|
|
// "Error in function '%s': '%s'"
|
|
WRMSG( HHC02219, "E", "fopen()", strerror( errno ));
|
|
}
|
|
else
|
|
{
|
|
// "Script file '%s' not found"
|
|
WRMSG( HHC01405, "E", script_path );
|
|
}
|
|
|
|
errno = save_errno; /* (restore error code for caller) */
|
|
free( scrname );
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
pCtl->scr_recursion++;
|
|
|
|
/* Read the first line into our statement buffer */
|
|
if (!script_abort( pCtl ) && !fgets( stmt, MAX_SCRIPT_STMT, fp ))
|
|
{
|
|
// "Script %d: begin processing file '%s'"
|
|
WRMSG( HHC02260, "I", pCtl->scr_id, script_path );
|
|
goto script_end;
|
|
}
|
|
|
|
#if defined(HAVE_OBJECT_REXX) || defined(HAVE_REGINA_REXX)
|
|
|
|
/* Skip past blanks to start of command */
|
|
for (p = stmt; isspace( (unsigned char)*p ); p++)
|
|
; /* (nop; do nothing) */
|
|
|
|
/* Execute REXX script if line begins with "Slash '/' Asterisk '*'" */
|
|
if (!script_abort( pCtl ) && !strncmp( p, "/*", 2 ))
|
|
{
|
|
char *rcmd[2] = { "exec", NULL };
|
|
rcmd[1] = script_path;
|
|
fclose( fp ); fp = NULL;
|
|
exec_cmd( 2, rcmd, NULL ); /* (synchronous) */
|
|
goto script_end;
|
|
}
|
|
#endif
|
|
|
|
// "Script %d: begin processing file '%s'"
|
|
WRMSG( HHC02260, "I", pCtl->scr_id, script_path );
|
|
|
|
do
|
|
{
|
|
/* Skip past blanks to start of statement */
|
|
for (p = stmt; isspace( (unsigned char)*p ); p++)
|
|
; /* (nop; do nothing) */
|
|
|
|
/* Remove trailing whitespace */
|
|
for (stmtlen = (int)strlen(p); stmtlen && isspace((unsigned char)p[stmtlen-1]); stmtlen--);
|
|
p[stmtlen] = 0;
|
|
|
|
/* Special handling for 'pause' and other statements */
|
|
if (do_special(NULL, NULL, pCtl, p))
|
|
continue;
|
|
|
|
/* Process statement as command */
|
|
panel_command( p );
|
|
}
|
|
while (!script_abort( pCtl ) && fgets( stmt, MAX_SCRIPT_STMT, fp ));
|
|
|
|
script_end:
|
|
|
|
/* Issue termination message and close file */
|
|
if (fp)
|
|
{
|
|
if (feof( fp ))
|
|
{
|
|
// "Script %d: file '%s' processing ended"
|
|
WRMSG( HHC02264, "I", pCtl->scr_id, script_path );
|
|
}
|
|
else /* (canceled, recursion, or i/o error) */
|
|
{
|
|
if (!(pCtl->scr_flags & SCR_ABORTED)) /* (no msg issued yet?) */
|
|
{
|
|
if (!script_abort( pCtl )) /* (not abort request?) */
|
|
{
|
|
// "Error in function '%s': '%s'"
|
|
WRMSG( HHC02219,"E", "read()", strerror( errno ));
|
|
pCtl->scr_flags |= SCR_ABORT;
|
|
}
|
|
|
|
// "Script %d: file '%s' aborted due to previous conditions"
|
|
WRMSG( HHC02265, "I", pCtl->scr_id, script_path );
|
|
pCtl->scr_flags |= SCR_ABORTED;
|
|
}
|
|
}
|
|
fclose( fp );
|
|
}
|
|
|
|
pCtl->scr_recursion--;
|
|
free( scrname );
|
|
return 0;
|
|
}
|
|
/* end process_script_file */
|
|
|
|
//#define LOGSCRTHREADBEGEND // TODO: make a decision about this
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Script processing thread - run script in background (internal) */
|
|
/*-------------------------------------------------------------------*/
|
|
static void *script_thread( void *arg )
|
|
{
|
|
char* argv[MAX_ARGS]; /* Command arguments array */
|
|
int argc; /* Number of cmd arguments */
|
|
int i; /* (work) */
|
|
SCRCTL* pCtl = NULL;
|
|
|
|
UNREFERENCED(arg);
|
|
|
|
#ifdef LOGSCRTHREADBEGEND
|
|
// "Thread id "TIDPAT", prio %2d, name '%s' started"
|
|
LOG_THREAD_BEGIN( SCRIPT_THREAD_NAME );
|
|
#endif
|
|
|
|
/* Retrieve our control entry */
|
|
pCtl = FindSCRCTL( thread_id() );
|
|
ASSERT( pCtl && pCtl->scr_cmdline ); /* (sanity check) */
|
|
|
|
/* Parse the command line into its individual arguments...
|
|
Note: original command line now sprinkled with nulls */
|
|
parse_args( pCtl->scr_cmdline, MAX_ARGS, argv, &argc );
|
|
ASSERT( argc > 1 ); /* (sanity check) */
|
|
|
|
/* Process each filename argument on this script command */
|
|
for (i=1; !script_abort( pCtl ) && i < argc; i++)
|
|
{
|
|
UpdSCRCTL( pCtl, argv[i] );
|
|
process_script_file( argv[i], false );
|
|
}
|
|
|
|
/* Remove entry from list and exit */
|
|
FreeSCRCTL( pCtl );
|
|
|
|
#ifdef LOGSCRTHREADBEGEND
|
|
// "Thread id "TIDPAT", prio %2d, name '%s' ended"
|
|
LOG_THREAD_BEGIN( SCRIPT_THREAD_NAME );
|
|
#endif
|
|
|
|
return NULL;
|
|
}
|
|
/* end script_thread */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* 'script' command handler (external) */
|
|
/*-------------------------------------------------------------------*/
|
|
int script_cmd( int argc, char* argv[], char* cmdline )
|
|
{
|
|
char* scr_cmdline = NULL; /* Copy of original command-line */
|
|
TID tid = thread_id();
|
|
SCRCTL* pCtl = NULL;
|
|
int rc = 0;
|
|
|
|
ASSERT( cmdline && *cmdline ); /* (sanity check) */
|
|
|
|
/* Display all running scripts if no arguments given */
|
|
if (argc <= 1)
|
|
{
|
|
ListScriptsIds();
|
|
return 0;
|
|
}
|
|
|
|
/* Find script processing control entry for this thead */
|
|
if ((pCtl = FindSCRCTL( tid )) != NULL)
|
|
{
|
|
/* This script command is issued from a script. It does not */
|
|
/* necessarily cause a recursion. */
|
|
int i, rc2 = 0;
|
|
for (i=1; !script_abort( pCtl ) && i < argc; i++)
|
|
{
|
|
UpdSCRCTL( pCtl, argv[i] );
|
|
rc = process_script_file( argv[i], false );
|
|
if (0 <= rc2 && 0 < rc) rc2 = MAX( rc, rc2 );
|
|
else if (0 > rc) rc2 = MIN( rc, rc2 );
|
|
}
|
|
return rc2;
|
|
}
|
|
|
|
/* Create control entry and add to list */
|
|
if (!(pCtl = NewSCRCTL( 0, argv[1] )))
|
|
return -1; /* (error msg already issued) */
|
|
|
|
/* Create a copy of the command line */
|
|
if (!(scr_cmdline = strdup( cmdline )))
|
|
{
|
|
// "Out of memory"
|
|
WRMSG( HHC00152, "E" );
|
|
FreeSCRCTL( pCtl );
|
|
return -1;
|
|
}
|
|
|
|
obtain_lock( &sysblk.scrlock );
|
|
pCtl->scr_cmdline = scr_cmdline;
|
|
|
|
/* Create the associated script processing thread */
|
|
if ((rc = create_thread( &pCtl->scr_tid, DETACHED,
|
|
script_thread, NULL, SCRIPT_THREAD_NAME )) != 0)
|
|
{
|
|
pCtl->scr_tid = 0; /* (deactivate) */
|
|
// "Error in function create_thread(): %s"
|
|
WRMSG( HHC00102, "E", strerror( rc ));
|
|
release_lock( &sysblk.scrlock );
|
|
FreeSCRCTL( pCtl );
|
|
return -1;
|
|
}
|
|
|
|
release_lock( &sysblk.scrlock );
|
|
|
|
return 0;
|
|
}
|
|
/* end script_cmd */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* $runtest command - invalid when entered as a Hercules command */
|
|
/*-------------------------------------------------------------------*/
|
|
int $runtest_cmd(int argc,char *argv[], char *cmdline)
|
|
{
|
|
UNREFERENCED( argc );
|
|
UNREFERENCED( argv );
|
|
UNREFERENCED( cmdline );
|
|
|
|
// "runtest is only valid as a scripting command"
|
|
WRMSG( HHC02337, "E" );
|
|
return -1;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* RunTest ABORT */
|
|
/*-------------------------------------------------------------------*/
|
|
static int test_abort( SCRCTL *pCtl )
|
|
{
|
|
// "Script %d: test: aborted"
|
|
WRMSG( HHC02331, "E", pCtl->scr_id );
|
|
panel_command( "stopall");
|
|
panel_command( "sysreset");
|
|
return -1;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* runtest script command - begin test and wait for completion */
|
|
/*-------------------------------------------------------------------*/
|
|
int runtest( SCRCTL *pCtl, char *cmdline, char *args )
|
|
{
|
|
char* p2 = NULL; /* Resolved symbol string */
|
|
struct timeval beg, now, dur; /* To calculate remaining time */
|
|
double secs = DEF_RUNTEST_DUR; /* Optional timeout in seconds */
|
|
U32 usecs; /* Same thing in microseconds */
|
|
U32 sleep_usecs; /* Remaining microseconds */
|
|
U32 elapsed_usecs; /* To calculate remaining time */
|
|
int rc; /* Return code from timed wait */
|
|
int dostart = 0; /* 0 = start test via "restart" */
|
|
/* 1 = start test via "start" */
|
|
UNREFERENCED( cmdline );
|
|
|
|
ASSERT( sysblk.scrtest ); /* How else did we get called? */
|
|
|
|
/* Parse optional RUNTEST command arguments. */
|
|
/* Syntax: RUNTEST [RESTART|START] [timeout] */
|
|
|
|
if (*args && *args != '#')
|
|
{
|
|
p2 = resolve_symbol_string( args );
|
|
if (p2)
|
|
args = p2;
|
|
|
|
if (isalpha( (unsigned char)args[0] )) /* [RESTART|START|<oldpsw>]? */
|
|
{
|
|
#define MAX_KW_LEN 15
|
|
char kw[ MAX_KW_LEN + 1 ] = {0};
|
|
char* pkw = NULL;
|
|
|
|
if (sscanf( args, "%"QSTR( MAX_KW_LEN )"s %lf", kw, &secs ))
|
|
{
|
|
if (!strcasecmp( kw, "start" ))
|
|
dostart = 1;
|
|
else if (!strcasecmp( kw, "restart" ))
|
|
; /* Do nothing */
|
|
else if (!set_restart(kw))
|
|
pkw = kw; /* Not valid restart */
|
|
}
|
|
else
|
|
pkw = args;
|
|
|
|
if (pkw)
|
|
{
|
|
// "Script %d: test: unknown runtest keyword: %s"
|
|
WRMSG( HHC02341, "E", pCtl->scr_id, pkw );
|
|
if (p2)
|
|
free( p2 );
|
|
return test_abort( pCtl );
|
|
}
|
|
|
|
/* Get past keyword to next argument */
|
|
args += strlen( kw );
|
|
}
|
|
|
|
if ( args[0] ) /* [timeout]? */
|
|
{
|
|
char without_comment[64];
|
|
char* pszComment;
|
|
BYTE c;
|
|
|
|
STRLCPY( without_comment, args );
|
|
|
|
pszComment = strchr( without_comment, '#' );
|
|
|
|
if (pszComment)
|
|
*pszComment = 0;
|
|
|
|
RTRIM( without_comment );
|
|
|
|
if (0
|
|
|| (rc = sscanf( without_comment, "%lf%c", &secs, &c )) != 1
|
|
|| secs < MIN_RUNTEST_DUR
|
|
|| secs > MAX_RUNTEST_DUR
|
|
)
|
|
{
|
|
int new_secs;
|
|
char badval[16];
|
|
|
|
MSGBUF( badval, "%s", without_comment );
|
|
|
|
if (rc != 1)
|
|
secs = DEF_RUNTEST_DUR;
|
|
else
|
|
if (secs < MIN_RUNTEST_DUR)
|
|
secs = MIN_RUNTEST_DUR;
|
|
else
|
|
if (secs > MAX_RUNTEST_DUR)
|
|
secs = MAX_RUNTEST_DUR;
|
|
else
|
|
secs = DEF_RUNTEST_DUR;
|
|
|
|
// NOTE: fails if secs < 0.5 (new_secs = 0)
|
|
// but we don't care.
|
|
new_secs = (int) secs;
|
|
|
|
// "Script %d: test: invalid timeout %s; set to %d instead"
|
|
WRMSG( HHC02335, "W", pCtl->scr_id, badval, new_secs );
|
|
}
|
|
}
|
|
|
|
if (p2)
|
|
free( p2 );
|
|
}
|
|
|
|
/* Apply adjustment factor */
|
|
if (sysblk.scrfactor)
|
|
secs *= sysblk.scrfactor;
|
|
|
|
/* Calculate maximum duration */
|
|
usecs = (U32) (secs * 1000000.0);
|
|
|
|
if (MLVL( VERBOSE ))
|
|
{
|
|
// "Script %d: test: test starting"
|
|
WRMSG( HHC02336, "I", pCtl->scr_id );
|
|
// "Script %d: test: duration limit: %"PRId32".%06"PRId32" seconds"
|
|
WRMSG( HHC02339, "I", pCtl->scr_id, usecs / 1000000,
|
|
usecs % 1000000 );
|
|
}
|
|
|
|
/* Press the restart or start button to start the test */
|
|
|
|
obtain_lock( &sysblk.scrlock );
|
|
sysblk.scrtest = 1; /*(reset)*/
|
|
release_lock( &sysblk.scrlock );
|
|
|
|
if (dostart)
|
|
rc = start_cmd_cpu( 0, NULL, NULL );
|
|
else
|
|
rc = restart_cmd( 0, NULL, NULL );
|
|
|
|
if (rc)
|
|
{
|
|
// "Script %d: test: [re]start failed"
|
|
WRMSG( HHC02330, "E", pCtl->scr_id );
|
|
return test_abort( pCtl );
|
|
}
|
|
|
|
if (MLVL( VERBOSE ))
|
|
// "Script %d: test: running..."
|
|
WRMSG( HHC02333, "I", pCtl->scr_id );
|
|
|
|
obtain_lock( &sysblk.scrlock );
|
|
gettimeofday( &beg, NULL );
|
|
for (;;)
|
|
{
|
|
/* Check for cancelled script */
|
|
if (pCtl && script_abort( pCtl ))
|
|
{
|
|
release_lock( &sysblk.scrlock );
|
|
return -1;
|
|
}
|
|
|
|
/* Has the test completed yet?
|
|
**
|
|
** Before test scripts are started the sysblk.scrtest
|
|
** counter is always reset to '1' to indicate testing
|
|
** mode is active (see further above). When each CPU
|
|
** completes its test (by either stopping or loading
|
|
** a disabled wait PSW) code in cpu.c then increments
|
|
** sysblk.scrtest. Only when sysblk.scrtest has been
|
|
** incremented past the number of configured CPUs is
|
|
** the test then considered to be complete.
|
|
*/
|
|
if (sysblk.scrtest > sysblk.cpus)
|
|
{
|
|
rc = 0;
|
|
break;
|
|
}
|
|
|
|
/* Calculate how long to continue waiting */
|
|
gettimeofday( &now, NULL );
|
|
timeval_subtract( &beg, &now, &dur );
|
|
elapsed_usecs = (dur.tv_sec * 1000000) + dur.tv_usec;
|
|
|
|
/* Is there any time remaining on the clock? */
|
|
if (elapsed_usecs >= usecs)
|
|
{
|
|
rc = ETIMEDOUT;
|
|
break;
|
|
}
|
|
|
|
/* Sleep until we're woken or we run out of time */
|
|
sleep_usecs = (usecs - elapsed_usecs);
|
|
timed_wait_condition_relative_usecs(
|
|
&sysblk.scrcond, &sysblk.scrlock, sleep_usecs, NULL );
|
|
}
|
|
gettimeofday( &now, NULL );
|
|
timeval_subtract( &beg, &now, &dur );
|
|
elapsed_usecs = (dur.tv_sec * 1000000) + dur.tv_usec;
|
|
release_lock( &sysblk.scrlock );
|
|
|
|
if (ETIMEDOUT == rc)
|
|
{
|
|
// "Script %d: test: timeout"
|
|
WRMSG( HHC02332, "E", pCtl->scr_id );
|
|
// "Script %d: test: actual duration: %"PRId32".%06"PRId32" seconds"
|
|
WRMSG( HHC02338, "I", pCtl->scr_id, elapsed_usecs / 1000000,
|
|
elapsed_usecs % 1000000 );
|
|
return test_abort( pCtl );
|
|
}
|
|
|
|
if (MLVL( VERBOSE ))
|
|
{
|
|
// "Script %d: test: test ended"
|
|
WRMSG( HHC02334, "I", pCtl->scr_id );
|
|
// "Script %d: test: actual duration: %"PRId32".%06"PRId32" seconds"
|
|
WRMSG( HHC02338, "I", pCtl->scr_id, elapsed_usecs / 1000000,
|
|
elapsed_usecs % 1000000 );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Set the restart PSW address to the contents of an old PSW. */
|
|
|
|
static int
|
|
set_restart(const char * s)
|
|
{
|
|
int i;
|
|
REGS *regs;
|
|
|
|
static const char * psws[] =
|
|
{
|
|
/* Maintain in order of assigned locations */
|
|
"external", "svc", "program", "machine", "io", 0
|
|
};
|
|
|
|
for (i = 0; ; i++)
|
|
{
|
|
if (!psws[i]) return 0;
|
|
if (!strcasecmp(s, psws[i])) break;
|
|
}
|
|
|
|
regs = sysblk.regs[sysblk.pcpu];
|
|
|
|
if (sysblk.arch_mode == ARCH_900_IDX)
|
|
{
|
|
PSA_900 * psa = regs->zpsa;
|
|
const int len = sizeof(psa->rstnew);
|
|
|
|
memcpy(psa->rstnew, psa->extold + i * len, len);
|
|
}
|
|
else
|
|
{
|
|
PSA_3XX * psa = regs->psa;
|
|
const int len = sizeof(psa->extold);
|
|
|
|
memcpy(psa->iplpsw, psa->extold + i * len, len);
|
|
}
|
|
return 1; /* OK */
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* returns: TRUE == stmt processed, FALSE == stmt NOT processed */
|
|
/*-------------------------------------------------------------------*/
|
|
static int
|
|
do_special(char *fname, int *inc_stmtnum, SCRCTL *pCtl, char *p)
|
|
{
|
|
struct timeval beg, now, dur; /* To calculate remaining time */
|
|
double secs; /* Seconds to pause processing */
|
|
U32 msecs; /* Same thing in milliseconds */
|
|
U32 usecs; /* Same thing in microseconds */
|
|
U32 elapsed_usecs; /* To calculate remaining time */
|
|
char* p2 = p; /* Work ptr for stmt parsing */
|
|
|
|
/* Determine if pause statement, special statement, or neither. */
|
|
if (strncasecmp( p2, "pause ", 6 ) == 0)
|
|
{
|
|
p2 += 6;
|
|
}
|
|
else
|
|
/* The runtest command is only valid in scripts not config files */
|
|
if (1
|
|
&& pCtl
|
|
&& sysblk.scrtest
|
|
&& strncasecmp( p2, "runtest", 7 ) == 0
|
|
&& (*(p2+7) == ' ' || *(p2+7) == '\0')
|
|
)
|
|
{
|
|
p2 += 7;
|
|
/* Skip past any blanks to the first argument, if any */
|
|
if (*p2)
|
|
while (*p2 == ' ')
|
|
++p2;
|
|
runtest( pCtl, p, p2 );
|
|
return TRUE;
|
|
}
|
|
else
|
|
return FALSE;
|
|
|
|
/* Determine maximum pause duration in seconds */
|
|
if (pCtl) /* only if script stmt; cfg file already did this */
|
|
{
|
|
char *p3 = resolve_symbol_string( p2 );
|
|
if (p3)
|
|
{
|
|
secs = atof( p3 );
|
|
free( p3 );
|
|
}
|
|
else
|
|
secs = atof( p2 );
|
|
}
|
|
else
|
|
secs = atof( p2 );
|
|
|
|
if (secs < MIN_PAUSE_TIMEOUT || secs > MAX_PAUSE_TIMEOUT)
|
|
{
|
|
if (fname)
|
|
// "Config file[%d] %s: error processing statement: %s"
|
|
WRMSG( HHC01441, "E", *inc_stmtnum, fname, "syntax error; statement ignored" );
|
|
else
|
|
// "Script %d: syntax error; statement ignored: %s"
|
|
WRMSG( HHC02261, "E", pCtl->scr_id, p );
|
|
return TRUE;
|
|
}
|
|
|
|
/* Apply adjustment factor */
|
|
if (sysblk.scrfactor)
|
|
secs *= sysblk.scrfactor;
|
|
|
|
/* Convert floating point seconds to other subsecond work values */
|
|
msecs = (U32) (secs * 1000.0);
|
|
|
|
if (MLVL( VERBOSE ))
|
|
{
|
|
if (fname)
|
|
// "Config file[%d] %s: processing paused for %d milliseconds..."
|
|
WRMSG( HHC02318, "I", *inc_stmtnum, fname, msecs );
|
|
else
|
|
// "Script %d: processing paused for %d milliseconds..."
|
|
WRMSG( HHC02262, "I", pCtl->scr_id, msecs );
|
|
}
|
|
|
|
/* Initialize pause start time */
|
|
gettimeofday( &beg, NULL );
|
|
|
|
obtain_lock( &sysblk.scrlock );
|
|
for (;;)
|
|
{
|
|
/* Check for cancelled script */
|
|
if (pCtl && script_abort( pCtl ))
|
|
{
|
|
release_lock( &sysblk.scrlock );
|
|
return TRUE;
|
|
}
|
|
|
|
/* Calculate how long to continue pausing */
|
|
gettimeofday( &now, NULL );
|
|
timeval_subtract( &beg, &now, &dur );
|
|
elapsed_usecs = (dur.tv_sec * 1000000) + dur.tv_usec;
|
|
|
|
/* Is there any time remaining on the clock? */
|
|
if (elapsed_usecs >= (msecs * 1000))
|
|
break;
|
|
|
|
/* Sleep until we're woken or we run out of time */
|
|
usecs = ((msecs * 1000) - elapsed_usecs);
|
|
timed_wait_condition_relative_usecs(
|
|
&sysblk.scrcond, &sysblk.scrlock, usecs, NULL );
|
|
}
|
|
release_lock( &sysblk.scrlock );
|
|
|
|
if (MLVL( VERBOSE ))
|
|
{
|
|
if (fname)
|
|
// "Config file[%d] %s: processing resumed..."
|
|
WRMSG( HHC02319, "I", *inc_stmtnum, fname );
|
|
else
|
|
// "Script %d: processing resumed..."
|
|
WRMSG( HHC02263, "I", pCtl->scr_id );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#endif /*!defined(_GEN_ARCH)*/
|