mirror of
https://github.com/SDL-Hercules-390/hyperion.git
synced 2026-04-08 21:23:51 +02:00
2219 lines
71 KiB
C
2219 lines
71 KiB
C
/* IMPL.C (C) Copyright Roger Bowler, 1999-2012 */
|
|
/* (C) and others 2013-2023 */
|
|
/* Hercules Initialization Module */
|
|
/* */
|
|
/* Released under "The Q Public License Version 1" */
|
|
/* (http://www.hercules-390.org/herclic.html) as modifications to */
|
|
/* Hercules. */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* This module initializes the Hercules S/370 or ESA/390 emulator. */
|
|
/* It builds the system configuration blocks, creates threads for */
|
|
/* central processors, HTTP server, logger task and activates the */
|
|
/* control panel which runs under the main thread when in foreground */
|
|
/* mode. */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
#include "hstdinc.h"
|
|
|
|
#define _IMPL_C_
|
|
#define _HENGINE_DLL_
|
|
|
|
#include "hercules.h"
|
|
#include "opcode.h"
|
|
#include "devtype.h"
|
|
#include "herc_getopt.h"
|
|
#include "hostinfo.h"
|
|
#include "history.h"
|
|
#include "hRexx.h"
|
|
#if defined( OPTION_W32_CTCI ) // (need tt32_get_default_iface)
|
|
#include "w32ctca.h"
|
|
#endif
|
|
#include "cckddasd.h" // (need cckd_gc_rpt_states)
|
|
|
|
static char shortopts[] =
|
|
|
|
"eh::f:o:r:dnb:vt::p:l:s:";
|
|
|
|
#if defined(HAVE_GETOPT_LONG)
|
|
static struct option longopts[] =
|
|
{
|
|
{ "externalgui", no_argument, NULL, 'e' },
|
|
{ "help", optional_argument, NULL, 'h' },
|
|
{ "version", no_argument, NULL, 'V' },
|
|
{ "config", required_argument, NULL, 'f' },
|
|
{ "output", required_argument, NULL, 'o' },
|
|
{ "logfile", required_argument, NULL, 'o' },
|
|
{ "rcfile", required_argument, NULL, 'r' },
|
|
{ "daemon", no_argument, NULL, 'd' }, // (deprecated!)
|
|
{ "NoUI", no_argument, NULL, 'n' }, // (use instead!)
|
|
{ "herclogo", required_argument, NULL, 'b' },
|
|
{ "verbose", no_argument, NULL, 'v' },
|
|
{ "test", optional_argument, NULL, 't' },
|
|
{ "modpath", required_argument, NULL, 'p' },
|
|
{ "ldmod", required_argument, NULL, 'l' },
|
|
{ "defsym", required_argument, NULL, 's' },
|
|
{ NULL, 0, NULL, 0 }
|
|
};
|
|
#endif
|
|
|
|
static LOGCALLBACK log_callback = NULL;
|
|
|
|
struct cfgandrcfile
|
|
{
|
|
const char * filename; /* Or NULL */
|
|
const char * const envname; /* Name of environ var to test */
|
|
const char * const defaultfile; /* Default file */
|
|
const char * const whatfile; /* config/restart, for message */
|
|
};
|
|
|
|
enum cfgorrc
|
|
{
|
|
want_cfg,
|
|
want_rc,
|
|
cfgorrccount
|
|
};
|
|
|
|
static struct cfgandrcfile cfgorrc[ cfgorrccount ] =
|
|
{
|
|
{ NULL, "HERCULES_CNF", "hercules.cnf", "Configuration", },
|
|
{ NULL, "HERCULES_RC", "hercules.rc", "Run Commands", },
|
|
};
|
|
|
|
#define MAX_MODS 50 /* Max mods to load */
|
|
static char* modnames[ MAX_MODS ] = {0}; /* ptrs to modnames */
|
|
static int modcount = 0; /* count of modnames */
|
|
|
|
/* forward define process_script_file (ISW20030220-3) */
|
|
extern int process_script_file( const char*, bool );
|
|
/* extern int quit_cmd(int argc, char *argv[],char *cmdline); */
|
|
|
|
/* Forward declarations: */
|
|
static void init_progname( int argc, char* argv[] );
|
|
|
|
static int process_args( int argc, char* argv[] );
|
|
#define PROCESS_ARGS_OK 0
|
|
#define PROCESS_ARGS_ERROR 1
|
|
#define PROCESS_ARGS_EXIT 2
|
|
/* End of forward declarations. */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Register a LOG callback */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void registerLogCallback( LOGCALLBACK cb )
|
|
{
|
|
log_callback = cb;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Subroutine to exit process after flushing stderr and stdout */
|
|
/*-------------------------------------------------------------------*/
|
|
static void delayed_exit (int exit_code)
|
|
{
|
|
UNREFERENCED(exit_code);
|
|
|
|
/* Delay exiting is to give the system
|
|
* time to display the error message. */
|
|
#if defined( _MSVC_ )
|
|
SetConsoleCtrlHandler( NULL, FALSE); // disable Ctrl-C intercept
|
|
#endif
|
|
sysblk.shutimmed = TRUE;
|
|
|
|
fflush(stderr);
|
|
fflush(stdout);
|
|
USLEEP(100000);
|
|
do_shutdown();
|
|
fflush(stderr);
|
|
fflush(stdout);
|
|
USLEEP(100000);
|
|
return;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* SIGINT signal handler */
|
|
/*-------------------------------------------------------------------*/
|
|
/* */
|
|
/* The SIGINT signal is sent to a process by its controlling */
|
|
/* terminal when a user wishes to interrupt the process. This */
|
|
/* is typically initiated by pressing Ctrl+C, but on some */
|
|
/* systems the "delete" character or "break" key can be used. */
|
|
/* */
|
|
/*-------------------------------------------------------------------*/
|
|
static void sigint_handler( int signo )
|
|
{
|
|
UNREFERENCED( signo );
|
|
|
|
signal( SIGINT, sigint_handler );
|
|
|
|
/* Ignore signal unless presented on console thread */
|
|
if (!equal_threads( thread_id(), sysblk.cnsltid ))
|
|
return;
|
|
|
|
/* Exit if previous SIGINT request was not actioned */
|
|
if (sysblk.sigintreq)
|
|
{
|
|
/* Release the configuration */
|
|
release_config( NULL );
|
|
delayed_exit(1);
|
|
}
|
|
|
|
/* Set SIGINT request pending flag */
|
|
sysblk.sigintreq = 1;
|
|
|
|
/* Activate instruction stepping */
|
|
sysblk.instbreak = 1;
|
|
SET_IC_TRACE;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* SIGTERM signal handler */
|
|
/*-------------------------------------------------------------------*/
|
|
/* */
|
|
/* The SIGTERM signal is sent to a process to request its */
|
|
/* termination. Unlike the SIGKILL signal, it can be caught */
|
|
/* and interpreted or ignored by the process. This allows the */
|
|
/* process to perform nice termination releasing resources */
|
|
/* and saving state if appropriate. SIGINT is nearly identical */
|
|
/* to SIGTERM. */
|
|
/* */
|
|
/*-------------------------------------------------------------------*/
|
|
static void sigterm_handler( int signo )
|
|
{
|
|
UNREFERENCED( signo );
|
|
|
|
signal( SIGTERM, sigterm_handler );
|
|
|
|
/* Ignore signal unless presented on main program (impl) thread */
|
|
if (!equal_threads( thread_id(), sysblk.impltid ))
|
|
return;
|
|
|
|
/* Initiate system shutdown */
|
|
do_shutdown();
|
|
}
|
|
|
|
#if defined( _MSVC_ )
|
|
/*-------------------------------------------------------------------*/
|
|
/* Perform immediate/emergency shutdown */
|
|
/*-------------------------------------------------------------------*/
|
|
static void do_emergency_shutdown()
|
|
{
|
|
sysblk.shutdown = TRUE;
|
|
|
|
if (!sysblk.shutimmed)
|
|
{
|
|
sysblk.shutimmed = TRUE;
|
|
do_shutdown();
|
|
}
|
|
else // (already in progress)
|
|
{
|
|
while (!sysblk.shutfini)
|
|
USLEEP(100000);
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Windows console control signal handler */
|
|
/*-------------------------------------------------------------------*/
|
|
static BOOL WINAPI console_ctrl_handler( DWORD signo )
|
|
{
|
|
switch ( signo )
|
|
{
|
|
///////////////////////////////////////////////////////////////
|
|
//
|
|
// PROGRAMMING NOTE
|
|
//
|
|
///////////////////////////////////////////////////////////////
|
|
//
|
|
// "SetConsoleCtrlHandler function HandlerRoutine Callback
|
|
// Function:"
|
|
//
|
|
// "Return Value:"
|
|
//
|
|
// "If the function handles the control signal, it
|
|
// should return TRUE."
|
|
//
|
|
// "CTRL_LOGOFF_EVENT:"
|
|
//
|
|
// "Note that this signal is received only by services.
|
|
// Interactive applications are terminated at logoff,
|
|
// so they are not present when the system sends this
|
|
// signal."
|
|
//
|
|
// "CTRL_SHUTDOWN_EVENT:"
|
|
//
|
|
// "Interactive applications are not present by the time
|
|
// the system sends this signal, therefore it can be
|
|
// received only be services in this situation."
|
|
//
|
|
// "CTRL_CLOSE_EVENT:"
|
|
//
|
|
// "... if the process does not respond within a certain
|
|
// time-out period (5 seconds for CTRL_CLOSE_EVENT..."
|
|
//
|
|
///////////////////////////////////////////////////////////////
|
|
//
|
|
// What this all boils down to is we'll never receive the
|
|
// logoff and shutdown signals (via this callback), and
|
|
// we only have a maximum of 5 seconds to return TRUE from
|
|
// the CTRL_CLOSE_EVENT signal. Thus, as normal shutdowns
|
|
// may likely take longer than 5 seconds and our goal is
|
|
// to try hard to shutdown Hercules as gracfully as we can,
|
|
// we are left with little choice but to always perform
|
|
// an immediate/emergency shutdown for CTRL_CLOSE_EVENT.
|
|
//
|
|
///////////////////////////////////////////////////////////////
|
|
|
|
case CTRL_BREAK_EVENT:
|
|
|
|
// "CTRL_BREAK_EVENT received: %s"
|
|
WRMSG( HHC01400, "I", "pressing interrupt key" );
|
|
|
|
OBTAIN_INTLOCK( NULL );
|
|
ON_IC_INTKEY;
|
|
WAKEUP_CPUS_MASK( sysblk.waiting_mask );
|
|
RELEASE_INTLOCK( NULL );
|
|
|
|
return TRUE;
|
|
|
|
case CTRL_C_EVENT:
|
|
|
|
if (!sysblk.shutimmed)
|
|
// "CTRL_C_EVENT received: %s"
|
|
WRMSG( HHC01401, "I", "initiating emergency shutdown" );
|
|
do_emergency_shutdown();
|
|
return TRUE;
|
|
|
|
|
|
case CTRL_CLOSE_EVENT:
|
|
|
|
if (!sysblk.shutimmed)
|
|
// "CTRL_CLOSE_EVENT received: %s"
|
|
WRMSG( HHC01402, "I", "initiating emergency shutdown" );
|
|
do_emergency_shutdown();
|
|
return TRUE;
|
|
|
|
default:
|
|
|
|
return FALSE; // (not handled; call next signal handler)
|
|
}
|
|
|
|
UNREACHABLE_CODE( return FALSE );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Windows hidden window message handler */
|
|
/*-------------------------------------------------------------------*/
|
|
static LRESULT CALLBACK MainWndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
switch (msg)
|
|
{
|
|
///////////////////////////////////////////////////////////////
|
|
//
|
|
// PROGRAMMING NOTE
|
|
//
|
|
///////////////////////////////////////////////////////////////
|
|
//
|
|
// "If an application returns FALSE in response to
|
|
// WM_QUERYENDSESSION, it still appears in the shutdown
|
|
// UI. Note that the system does not allow console
|
|
// applications or applications without a visible window
|
|
// to cancel shutdown. These applications are automatically
|
|
// terminated if they do not respond to WM_QUERYENDSESSION
|
|
// or WM_ENDSESSION within 5 seconds or if they return FALSE
|
|
// in response to WM_QUERYENDSESSION."
|
|
//
|
|
///////////////////////////////////////////////////////////////
|
|
//
|
|
// What this all boils down to is we can NEVER prevent the
|
|
// user from logging off or shutting down since not only are
|
|
// we a console application but our window is created invisible
|
|
// as well. Thus we only have a maximum of 5 seconds to return
|
|
// TRUE from WM_QUERYENDSESSION or return 0 from WM_ENDSESSION,
|
|
// and since a normal shutdown may likely take longer than 5
|
|
// seconds and our goal is to try hard to shutdown Hercules as
|
|
// gracfully as possible, we are left with little choice but to
|
|
// perform an immediate emergency shutdown once we receive the
|
|
// WM_ENDSESSION message with a WPARAM value of TRUE.
|
|
//
|
|
///////////////////////////////////////////////////////////////
|
|
|
|
case WM_QUERYENDSESSION:
|
|
|
|
// "%s received: %s"
|
|
WRMSG( HHC01403, "I", "WM_QUERYENDSESSION", "allow" );
|
|
return TRUE; // Vote "YES"... (we have no choice!)
|
|
|
|
case WM_ENDSESSION:
|
|
|
|
if (!wParam) // FALSE? (session not really ending?)
|
|
{
|
|
// Some other application (or the user themselves)
|
|
// has aborted the logoff or system shutdown...
|
|
|
|
// "%s received: %s"
|
|
WRMSG( HHC01403, "I", "WM_ENDSESSION", "aborted" );
|
|
return 0; // (message processed)
|
|
}
|
|
|
|
// User is logging off or the system is being shutdown.
|
|
// We have a maximum of 5 seconds to shutdown Hercules.
|
|
|
|
// "%s received: %s"
|
|
WRMSG( HHC01403, "I", "WM_ENDSESSION", "initiating emergency shutdown" );
|
|
do_emergency_shutdown();
|
|
return 0; // (message handled)
|
|
|
|
case WM_POWERBROADCAST:
|
|
|
|
// Notifies applications that a power-management event
|
|
// has occurred.
|
|
|
|
switch (wParam)
|
|
{
|
|
case PBT_APMSUSPEND:
|
|
|
|
// Notifies applications that the computer
|
|
// is about to enter a suspended state.
|
|
|
|
sysblk.sys_suspended = true;
|
|
sysblk.sys_resumed = false;
|
|
break;
|
|
|
|
case PBT_APMRESUMESUSPEND:
|
|
|
|
// Notifies applications that the system has resumed
|
|
// operation after being suspended.
|
|
|
|
/*
|
|
The below special "FALLTHRU" comment lets GCC know that we are
|
|
purposely falling through to the next switch case and is needed
|
|
in order to suppress the warning that GCC would otherwise issue.
|
|
*/
|
|
/* FALLTHRU */
|
|
|
|
case PBT_APMRESUMEAUTOMATIC:
|
|
|
|
// Notifies applications that the computer has woken up
|
|
// automatically to handle an event.
|
|
|
|
sysblk.sys_suspended = false;
|
|
sysblk.sys_resumed = true;
|
|
break;
|
|
|
|
default:
|
|
|
|
break; /* (do nothing) */
|
|
}
|
|
|
|
/*
|
|
The below special "FALLTHRU" comment lets GCC know that we are
|
|
purposely falling through to the next switch case and is needed
|
|
in order to suppress the warning that GCC would otherwise issue.
|
|
*/
|
|
/* FALLTHRU */
|
|
|
|
default:
|
|
|
|
return DefWindowProc( hWnd, msg, wParam, lParam );
|
|
}
|
|
|
|
UNREACHABLE_CODE( return 0 );
|
|
}
|
|
|
|
// Create invisible message handling window...
|
|
|
|
HANDLE g_hWndEvt = NULL; // (temporary window creation event)
|
|
HWND g_hMsgWnd = NULL; // (window handle of message window)
|
|
|
|
static void* WinMsgThread( void* arg )
|
|
{
|
|
WNDCLASS wc = {0};
|
|
|
|
UNREFERENCED( arg );
|
|
|
|
wc.lpfnWndProc = MainWndProc;
|
|
wc.hInstance = GetModuleHandle(0);
|
|
wc.lpszClassName = "Hercules";
|
|
|
|
RegisterClass( &wc );
|
|
|
|
g_hMsgWnd = CreateWindowEx( 0,
|
|
"Hercules", "Hercules",
|
|
WS_OVERLAPPEDWINDOW,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
NULL, NULL,
|
|
GetModuleHandle(0), NULL );
|
|
|
|
SetEvent( g_hWndEvt ); // (indicate create window completed)
|
|
|
|
if (g_hMsgWnd) // (pump messages if window successfully created)
|
|
{
|
|
MSG msg;
|
|
while (GetMessage( &msg, NULL , 0 , 0 ))
|
|
{
|
|
TranslateMessage ( &msg );
|
|
DispatchMessage ( &msg );
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
#endif /* defined( _MSVC_ ) */
|
|
|
|
#if defined( OPTION_WATCHDOG )
|
|
/*-------------------------------------------------------------------*/
|
|
/* watchdog_thread - monitor system for deadlocks */
|
|
/*-------------------------------------------------------------------*/
|
|
static void* watchdog_thread( void* arg )
|
|
{
|
|
REGS* regs;
|
|
S64 savecount[ MAX_CPU_ENGS ];
|
|
int cpu;
|
|
int sleep_seconds = WATCHDOG_SECS;
|
|
int sleep_secs2nd = 3;
|
|
int slept_secs;
|
|
int rc;
|
|
|
|
bool deadlock_reported = false;
|
|
bool hung_cpu_reported = false;
|
|
|
|
CPU_BITMAP hung_cpus_mask = 0;
|
|
|
|
UNREFERENCED( arg );
|
|
|
|
LOG_THREAD_BEGIN( WATCHDOG_THREAD_NAME );
|
|
|
|
for (cpu=0; cpu < sysblk.maxcpu; cpu++)
|
|
savecount[ cpu ] = -1;
|
|
|
|
/* Set watchdog priority LOWER than the CPU thread priority
|
|
such that it will not invalidly detect an inoperable CPU
|
|
*/
|
|
SET_THREAD_PRIORITY( MAX( sysblk.minprio, sysblk.cpuprio - 1 ), sysblk.qos_user_initiated);
|
|
UNREFERENCED(rc);
|
|
|
|
do
|
|
{
|
|
/* PROGRAMMING NOTE: sleeping in many small increments (rather
|
|
than one large sleep) prevents problems that can occur when
|
|
the system resumes (awakens) after having been suspended.
|
|
(GH Issue #458 "Hercules crash after resume from suspend")
|
|
*/
|
|
for (slept_secs=0; slept_secs < sleep_seconds; ++slept_secs)
|
|
{
|
|
SLEEP( 1 ); /* (sleep one second at a time) */
|
|
|
|
#if defined( _MSVC_ )
|
|
/* Start over again upon resume from suspend. This should
|
|
hopefully resolve GitHub Issue #489 "Hercules 4.4.1
|
|
crashes after OSA failure" by preventing malfunctioning
|
|
CPU false positives.
|
|
*/
|
|
if (sysblk.sys_suspended || sysblk.sys_resumed)
|
|
{
|
|
/* We're either being suspended or resumed */
|
|
sleep_seconds = WATCHDOG_SECS;
|
|
sleep_secs2nd = 3;
|
|
slept_secs = 0;
|
|
hung_cpus_mask = 0;
|
|
|
|
/* If being resumed, reset our flags to normal */
|
|
if (!sysblk.sys_suspended && sysblk.sys_resumed)
|
|
{
|
|
sysblk.sys_suspended = false;
|
|
sysblk.sys_resumed = false;
|
|
}
|
|
|
|
continue; /* (start over) */
|
|
}
|
|
#endif
|
|
} /* (end for (slept_secs ...) */
|
|
|
|
#if defined( _MSVC_ )
|
|
|
|
// If watchdog is NOT allowed while debugging (the default),
|
|
// then disable all watchdog logic while debugger is attached.
|
|
//
|
|
// This allows the developer to debug other areas of Hercules
|
|
// without the watchdog interfering with their debugging.
|
|
//
|
|
// Otherwise, if watchdog *IS* allowed while debugging (set
|
|
// via "$TEST WD YES" command), proceed with normal watchdog
|
|
// processing...
|
|
|
|
if (!sysblk.allow_wd_debugging && IsDebuggerPresent())
|
|
continue; // (neuter watchdog while they're debugging)
|
|
#endif
|
|
|
|
/* Check for and report any deadlocks */
|
|
if (hthread_report_deadlocks( deadlock_reported ? NULL : "S" ))
|
|
{
|
|
/*****************************************************/
|
|
/* DEADLOCK DETECTED! */
|
|
/*****************************************************/
|
|
|
|
if (!deadlock_reported)
|
|
{
|
|
// "DEADLOCK!"
|
|
WRMSG( HHC90024, "S" );
|
|
HDC1( debug_watchdog_signal, NULL );
|
|
}
|
|
deadlock_reported = true;
|
|
}
|
|
|
|
for (cpu=0; cpu < sysblk.maxcpu; cpu++)
|
|
{
|
|
/* We're only interested in ONLINE and STARTED CPUs */
|
|
if (0
|
|
|| !IS_CPU_ONLINE( cpu )
|
|
|| (regs = sysblk.regs[ cpu ])->cpustate != CPUSTATE_STARTED
|
|
)
|
|
{
|
|
/* CPU not ONLINE or not STARTED */
|
|
savecount[ cpu ] = -1;
|
|
continue;
|
|
}
|
|
|
|
/* CPU is ONLINE and STARTED. Now check to see if it's
|
|
maybe in a WAITSTATE. If so, we're not interested.
|
|
*/
|
|
if (0
|
|
|| WAITSTATE( ®s->psw )
|
|
#if defined( _FEATURE_WAITSTATE_ASSIST )
|
|
|| (1
|
|
&& regs->sie_active
|
|
&& WAITSTATE( &GUESTREGS->psw )
|
|
)
|
|
#endif
|
|
)
|
|
{
|
|
/* CPU is in a WAITSTATE */
|
|
savecount[ cpu ] = -1;
|
|
continue;
|
|
}
|
|
|
|
/* We have found a running CPU that should be executing
|
|
instructions. Compare its current instruction count
|
|
with our previously saved value. If they're different
|
|
then it has obviously executed SOME instructions and
|
|
all is well. Save its current instruction counter and
|
|
move on the next CPU. This one appears to be healthy.
|
|
*/
|
|
if (INSTCOUNT( regs ) != (U64) savecount[ cpu ])
|
|
{
|
|
/* Save updated instruction count for next time */
|
|
savecount[ cpu ] = INSTCOUNT( regs );
|
|
continue;
|
|
}
|
|
|
|
/*****************************************************/
|
|
/* MALFUNCTIONING CPU DETECTED! */
|
|
/*****************************************************/
|
|
|
|
hung_cpus_mask |= CPU_BIT( cpu );
|
|
}
|
|
|
|
/* If any hung CPUs were detected, do a second pass in
|
|
case there is another CPU that also stopped executing
|
|
instructions a few seconds after the first one did.
|
|
*/
|
|
if (hung_cpus_mask && sleep_seconds != sleep_secs2nd)
|
|
{
|
|
sleep_seconds = sleep_secs2nd;
|
|
continue;
|
|
}
|
|
|
|
/* Report all hung CPUs all at the same time */
|
|
if (hung_cpus_mask && !hung_cpu_reported)
|
|
{
|
|
for (cpu=0; cpu < sysblk.maxcpu; cpu++)
|
|
{
|
|
if (hung_cpus_mask & CPU_BIT( cpu ))
|
|
{
|
|
// "PROCESSOR %s%02X APPEARS TO BE HUNG!"
|
|
WRMSG( HHC00822, "S", PTYPSTR( cpu ), cpu );
|
|
HDC1( debug_watchdog_signal, sysblk.regs[ cpu ]);
|
|
}
|
|
}
|
|
|
|
hung_cpu_reported = true;
|
|
}
|
|
|
|
/* Create a crash dump if any problems were detected */
|
|
if (deadlock_reported || hung_cpu_reported)
|
|
{
|
|
#if defined( _MSVC_ )
|
|
|
|
// Give developer time to attach a debugger before crashing
|
|
// If they do so, then prevent the crash from occurring as
|
|
// long as their debugger is still attached, but once they
|
|
// detach their debugger, then go ahead and allow the crash
|
|
|
|
if (!sysblk.allow_wd_debugging)
|
|
{
|
|
static bool didwait = false;
|
|
|
|
if (!didwait)
|
|
{
|
|
// "You have %d seconds to attach a debugger before crash dump will be taken!"
|
|
WRMSG( HHC00823, "S", WAIT_FOR_DEBUGGER_SECS );
|
|
|
|
// Wait for them to attach a debugger if desired...
|
|
{
|
|
int i;
|
|
for (i=0; !IsDebuggerPresent() && i < WAIT_FOR_DEBUGGER_SECS; ++i)
|
|
SLEEP( 1 );
|
|
}
|
|
|
|
didwait = true;
|
|
}
|
|
|
|
// Don't crash if there is now a debugger attached
|
|
if (IsDebuggerPresent())
|
|
{
|
|
// "Debugger attached! NOT crashing!"
|
|
WRMSG( HHC00824, "S" );
|
|
continue; // (they're still debugging; prevent crashing)
|
|
}
|
|
}
|
|
|
|
// "Creating crash dump!"
|
|
WRMSG( HHC00825, "S" );
|
|
|
|
#endif // defined( _MSVC_ )
|
|
|
|
/* Display additional debugging information */
|
|
panel_command( "*" );
|
|
panel_command( "*" );
|
|
panel_command( "*" );
|
|
panel_command( "ptt" );
|
|
panel_command( "*" );
|
|
panel_command( "*" );
|
|
panel_command( "*" );
|
|
panel_command( "ipending" );
|
|
panel_command( "*" );
|
|
panel_command( "*" );
|
|
panel_command( "*" );
|
|
panel_command( "threads waiting sort tod" );
|
|
panel_command( "*" );
|
|
panel_command( "*" );
|
|
panel_command( "*" );
|
|
panel_command( "locks held sort tod" );
|
|
panel_command( "*" );
|
|
panel_command( "*" );
|
|
panel_command( "*" );
|
|
|
|
/* Display the instruction each hung CPU was executing */
|
|
if (hung_cpus_mask)
|
|
{
|
|
BYTE* ip;
|
|
REGS* regs;
|
|
|
|
for (cpu=0; cpu < sysblk.maxcpu; cpu++)
|
|
{
|
|
if (hung_cpus_mask & CPU_BIT( cpu ))
|
|
{
|
|
/* Backup to actual instruction being executed */
|
|
regs = sysblk.regs[ cpu ];
|
|
SET_PSW_IA_AND_MAYBE_IP( regs, PSW_IA_FROM_IP( regs, -REAL_ILC( regs )));
|
|
|
|
/* Display instruction that appears to be hung */
|
|
ip = regs->ip < regs->aip ? regs->inst : regs->ip;
|
|
ARCH_DEP( display_inst )( regs, ip );
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Give logger thread time to log messages */
|
|
SLEEP(1);
|
|
|
|
/* Create the crash dump for offline analysis */
|
|
CRASH();
|
|
}
|
|
}
|
|
while (!sysblk.shutdown);
|
|
|
|
LOG_THREAD_END( WATCHDOG_THREAD_NAME );
|
|
|
|
return NULL;
|
|
}
|
|
#endif /* defined( OPTION_WATCHDOG ) */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Herclin (plain line mode Hercules) message callback function */
|
|
/*-------------------------------------------------------------------*/
|
|
void* log_do_callback( void* dummy )
|
|
{
|
|
char* msgbuf;
|
|
int msglen;
|
|
int msgidx = -1;
|
|
|
|
UNREFERENCED( dummy );
|
|
|
|
while (!sysblk.shutfini && logger_isactive())
|
|
{
|
|
msglen = log_read( &msgbuf, &msgidx, LOG_NOBLOCK );
|
|
|
|
if (msglen)
|
|
{
|
|
log_callback( msgbuf, msglen );
|
|
continue;
|
|
}
|
|
|
|
/* wait a bit for new message(s) to arrive before retrying */
|
|
USLEEP( PANEL_REFRESH_RATE_FAST * 1000 );
|
|
}
|
|
|
|
/* Let them know logger thread has ended */
|
|
log_callback( NULL, 0 );
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Return panel command handler to herclin line mode hercules */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT COMMANDHANDLER getCommandHandler()
|
|
{
|
|
return (panel_command);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Process .RC file thread. */
|
|
/* Called synchronously when in No UI mode. Else asynchronously. */
|
|
/*-------------------------------------------------------------------*/
|
|
static void* process_rc_file( void* dummy )
|
|
{
|
|
char pathname[ MAX_PATH ];
|
|
|
|
UNREFERENCED( dummy );
|
|
|
|
/* Obtain the name of the hercules.rc file or default */
|
|
if (0
|
|
/* Neither '-r' nor "HERCULES_RC" environment variable given */
|
|
|| !cfgorrc[want_rc].filename
|
|
|| !cfgorrc[want_rc].filename[0]
|
|
)
|
|
{
|
|
/* Check for "hercules.rc" file in current directory */
|
|
struct stat st;
|
|
if (stat( "hercules.rc", &st ) == 0)
|
|
cfgorrc[want_rc].filename = "hercules.rc";
|
|
}
|
|
|
|
/* If we have a hercules.rc file, process it */
|
|
if (1
|
|
&& cfgorrc[want_rc].filename
|
|
&& cfgorrc[want_rc].filename[0]
|
|
&& strcasecmp( cfgorrc[want_rc].filename, "None" ) != 0
|
|
)
|
|
{
|
|
hostpath( pathname, cfgorrc[want_rc].filename, sizeof( pathname ));
|
|
|
|
#if 1 // ZZ FIXME: THIS NEEDS TO GO
|
|
|
|
/* Wait for panel thread to engage */
|
|
if (!sysblk.NoUI_mode)
|
|
while (!sysblk.panel_init)
|
|
USLEEP( 10 * 1000 );
|
|
|
|
#endif // ZZ FIXME: THIS NEEDS TO GO
|
|
|
|
/* Run the script processor for this file */
|
|
process_script_file( pathname, true );
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Display cmdline arguments help */
|
|
/*-------------------------------------------------------------------*/
|
|
static void arghelp()
|
|
{
|
|
char pgm[ MAX_PATH ];
|
|
char* strtok_str = NULL;
|
|
|
|
STRLCPY( pgm, sysblk.hercules_pgmname );
|
|
|
|
// "Usage: %s [--help[=SHORT|LONG|VERSION|BUILD]] -f config-filename|\"none\" [-o logfile-name] [-r rcfile-name] [-n] [-b logo-filename] [-s sym=val] [-t [factor]] [-p dyn-load-dir] [[-l dynmod-to-load]...] [> logfile]"
|
|
WRMSG( HHC01407, "S", strtok_r( pgm, ".", &strtok_str ) );
|
|
|
|
fflush( stderr );
|
|
fflush( stdout );
|
|
USLEEP( 100000 );
|
|
}
|
|
|
|
/* Functions in module skey.h/.c, needed by impl.c */
|
|
|
|
extern BYTE s370_get_storage_key( U64 abs );
|
|
extern BYTE s390_get_storage_key( U64 abs );
|
|
extern BYTE z900_get_storage_key( U64 abs );
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Check if host "PCLMULQDQ" instruction is available */
|
|
/*-------------------------------------------------------------------*/
|
|
#if defined( _MSVC_ )
|
|
|
|
static void is_PCLMULQDQ_available()
|
|
{
|
|
QW mm1, mm2, acc;
|
|
U64 m1 = 1, m2 = 2;
|
|
|
|
mm1.v = _mm_setzero_si128();
|
|
mm1.D.L.D = m1;
|
|
mm2.v = _mm_setzero_si128();
|
|
mm2.D.L.D = m2;
|
|
|
|
__try
|
|
{
|
|
acc.v = _mm_clmulepi64_si128 ( mm1.v, mm2.v, 0);
|
|
sysblk.have_PCLMULQDQ = true;
|
|
}
|
|
__except( EXCEPTION_EXECUTE_HANDLER )
|
|
{
|
|
sysblk.have_PCLMULQDQ = false;
|
|
}
|
|
}
|
|
|
|
#else // !defined( _MSVC_ ), i.e. Linux
|
|
|
|
#if defined( _GCC_SSE2_ ) && defined( HAVE_SIGNAL_HANDLING ) && defined( FEATURE_HW_CLMUL )
|
|
|
|
static struct sigaction sa_CRASH = {0};
|
|
static struct sigaction sa_SIGILL = {0};
|
|
static jmp_buf jmpbuff;
|
|
|
|
static void crash_signal_handler( int signo )
|
|
{
|
|
UNREFERENCED( signo );
|
|
sysblk.have_PCLMULQDQ = false;
|
|
longjmp( jmpbuff, 4 );
|
|
}
|
|
|
|
/* Disable optimization */
|
|
#if defined( __clang__ )
|
|
#pragma clang optimize off
|
|
#else
|
|
#pragma GCC push_options
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC optimize ("-O0")
|
|
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
|
#endif
|
|
|
|
static void is_PCLMULQDQ_available()
|
|
{
|
|
/* Install Illegal-Instruction (SIGILL) crash handler */
|
|
sa_CRASH.sa_handler = &crash_signal_handler;
|
|
sigaction( SIGILL, &sa_CRASH, &sa_SIGILL );
|
|
|
|
/* Try the problematic code... */
|
|
if (setjmp( jmpbuff ) == 0)
|
|
{
|
|
/* PROGRAMMING NOTE! Optimizations MUST be disabled for this
|
|
section of code in order for it to work correctly! Otherwise
|
|
the optimizer will elide the intrinsic altogether due to the
|
|
'acc' variable never being referenced! (causing the 'have'
|
|
flag to incorrectly be always set to true!)
|
|
*/
|
|
QW mm1, mm2, acc;
|
|
U64 m1 = 1, m2 = 2;
|
|
|
|
mm1.v = _mm_setzero_si128();
|
|
mm1.D.L.D = m1;
|
|
mm2.v = _mm_setzero_si128();
|
|
mm2.D.L.D = m2;
|
|
|
|
acc.v = _mm_clmulepi64_si128 ( mm1.v, mm2.v, 0);
|
|
sysblk.have_PCLMULQDQ = true;
|
|
}
|
|
else // (only executed if we crashed)
|
|
{
|
|
sysblk.have_PCLMULQDQ = false;
|
|
}
|
|
|
|
/* Restore original Illegal-Instruction (SIGILL) crash handler */
|
|
sigaction( SIGILL, &sa_SIGILL, 0 );
|
|
}
|
|
|
|
/* Re-enable optimization */
|
|
#if defined( __clang__ )
|
|
#pragma clang optimize on
|
|
#else
|
|
#pragma GCC diagnostic pop
|
|
#pragma GCC pop_options
|
|
#endif
|
|
|
|
#else // !defined( _GCC_SSE2_ ) || !defined( HAVE_SIGNAL_HANDLING ) || !defined( FEATURE_HW_CLMUL )
|
|
|
|
/* No way to know without SSE2 and signal handling, so play it safe! */
|
|
static void is_PCLMULQDQ_available()
|
|
{
|
|
sysblk.have_PCLMULQDQ = false; /* (safest default) */
|
|
}
|
|
|
|
#endif // defined( _GCC_SSE2_ ) && defined( HAVE_SIGNAL_HANDLING )
|
|
#endif /* Windows or Linux */
|
|
|
|
/* Check if various host instructions are available or not */
|
|
static void check_host_instruction_availability()
|
|
{
|
|
/* Check availability of each individual host instruction first */
|
|
is_PCLMULQDQ_available();
|
|
// is_XXXXXXXXX_available();
|
|
|
|
/* Then report all of the ones that aren't available */
|
|
// "WARNING: Host does not support the '%s' instruction"
|
|
if (!sysblk.have_PCLMULQDQ) WRMSG( HHC00026, "W", "PCLMULQDQ" );
|
|
// if (!sysblk.have_XXXXXXXXX) WRMSG( HHC00026, "W", "XXXXXXXXX" );
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* IMPL main entry point */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT int impl( int argc, char* argv[] )
|
|
{
|
|
TID rctid; /* RC file thread identifier */
|
|
TID logcbtid; /* RC file thread identifier */
|
|
int rc, maxprio, minprio;
|
|
|
|
SET_THREAD_NAME( IMPL_THREAD_NAME );
|
|
|
|
/* Seed the pseudo-random number generator */
|
|
init_random();
|
|
|
|
/* Save minprio/maxprio, which were set in bootstrap.c when it
|
|
called SET_THREAD_NAME at or near the beginning of main().
|
|
*/
|
|
minprio = sysblk.minprio;
|
|
maxprio = sysblk.maxprio;
|
|
|
|
/* Clear the system configuration block (SYSBLK) to zero */
|
|
memset( &sysblk, 0, sizeof( SYSBLK ) );
|
|
|
|
/* Restore saved minprio/maxprio into SYSBLK */
|
|
sysblk.minprio = minprio;
|
|
sysblk.maxprio = maxprio;
|
|
|
|
// Check if, and remember, if debugger is present...
|
|
// (must be done AFTER sysblk has been set to zero)
|
|
check_if_debugger_is_present();
|
|
|
|
/* Lock SYSBLK into memory since it's referenced so frequently.
|
|
Note that the call could fail when the working set is small
|
|
but that's okay. We did our best. Locking it isn't critical.
|
|
*/
|
|
MLOCK( &sysblk, sizeof( SYSBLK ));
|
|
|
|
#if defined (_MSVC_)
|
|
_setmaxstdio(2048);
|
|
#endif
|
|
|
|
INIT_BLOCK_HEADER_TRAILER( (&sysblk), SYSBLK );
|
|
|
|
// Set some defaults
|
|
sysblk.msglvl = DEFAULT_MLVL;
|
|
sysblk.logoptnotime = 0;
|
|
sysblk.logoptnodate = 1;
|
|
|
|
sysblk.num_pfxs = (int) strlen( DEF_CMDPREFIXES );
|
|
sysblk.cmd_pfxs = malloc( sysblk.num_pfxs );
|
|
sysblk.used_pfxs = malloc( sysblk.num_pfxs );
|
|
|
|
memcpy( sysblk.cmd_pfxs, DEF_CMDPREFIXES, sysblk.num_pfxs );
|
|
memset( sysblk.used_pfxs, 0, sysblk.num_pfxs );
|
|
|
|
/* Initialize program name and version strings arrays */
|
|
init_progname( argc, argv );
|
|
init_sysblk_version_str_arrays( NULL );
|
|
|
|
/* Process two common Unix options right here to avoid displaying
|
|
a bunch of unrelated threading messages on exit which just clutter
|
|
up the output and hide the real information.
|
|
|
|
Yes, this is a kludge, but it's not the only one in this thing.
|
|
*/
|
|
if (argc == 2 && strcmp(argv[1], "--version") == 0)
|
|
{
|
|
display_version(stdout, 0, NULL);
|
|
return 0;
|
|
}
|
|
|
|
if (argc == 2 && strcmp(argv[1], "--usage") == 0)
|
|
{
|
|
arghelp();
|
|
return 0;
|
|
}
|
|
|
|
/* Initialize SETMODE and set user authority */
|
|
SETMODE( INIT );
|
|
|
|
/* Remain compatible with older external gui versions */
|
|
if (argc >= 1 && strncmp( argv[argc-1], "EXTERNALGUI", 11 ) == 0)
|
|
{
|
|
extgui = TRUE;
|
|
argc--;
|
|
}
|
|
|
|
/* Initialize Hercules Threading package */
|
|
hthreads_internal_init();
|
|
|
|
/* Initialize 'hostinfo' BEFORE display_version is called */
|
|
init_hostinfo( &hostinfo );
|
|
|
|
#ifdef _MSVC_
|
|
/* Initialize sockets package */
|
|
VERIFY( socket_init() == 0 );
|
|
#endif
|
|
|
|
/* Ensure hdl_atexit is called in case of shutdown.
|
|
hdl_atexit ensures entries are called only once.
|
|
*/
|
|
atexit( hdl_atexit );
|
|
|
|
set_symbol( "VERSION", VERSION);
|
|
set_symbol( "BDATE", __DATE__ );
|
|
set_symbol( "BTIME", __TIME__ );
|
|
|
|
{
|
|
char num_procs[64];
|
|
|
|
if ( hostinfo.num_packages != 0 &&
|
|
hostinfo.num_physical_cpu != 0 &&
|
|
hostinfo.num_logical_cpu != 0 )
|
|
{
|
|
MSGBUF( num_procs, "LP=%d, Cores=%d, CPUs=%d", hostinfo.num_logical_cpu,
|
|
hostinfo.num_physical_cpu, hostinfo.num_packages );
|
|
}
|
|
else
|
|
{
|
|
if ( hostinfo.num_procs > 1 )
|
|
MSGBUF( num_procs, "MP=%d", hostinfo.num_procs );
|
|
else if ( hostinfo.num_procs == 1 )
|
|
STRLCPY( num_procs, "UP" );
|
|
else
|
|
STRLCPY( num_procs, "" );
|
|
}
|
|
|
|
set_symbol( "HOSTNAME", hostinfo.nodename );
|
|
set_symbol( "HOSTOS", hostinfo.sysname );
|
|
set_symbol( "HOSTOSREL", hostinfo.release );
|
|
set_symbol( "HOSTOSVER", hostinfo.version );
|
|
set_symbol( "HOSTARCH", hostinfo.machine );
|
|
set_symbol( "HOSTNUMCPUS", num_procs );
|
|
}
|
|
|
|
set_symbol( "MODNAME", sysblk.hercules_pgmname );
|
|
set_symbol( "MODPATH", sysblk.hercules_pgmpath );
|
|
|
|
sysblk.sysgroup = DEFAULT_SYSGROUP;
|
|
|
|
/* set default console port addresses */
|
|
sysblk.cnslport = strdup("3270");
|
|
|
|
/* Initialize automatic creation of missing tape file to default */
|
|
sysblk.auto_tape_create = DEF_AUTO_TAPE_CREATE;
|
|
|
|
/* Default command separator is OFF (disabled) */
|
|
sysblk.cmdsep = 0;
|
|
|
|
#if defined(_FEATURE_SYSTEM_CONSOLE)
|
|
/* set default for scpecho to TRUE */
|
|
sysblk.scpecho = TRUE;
|
|
|
|
/* set fault for scpimply to FALSE */
|
|
sysblk.scpimply = FALSE;
|
|
#endif
|
|
|
|
/* set default system state to reset */
|
|
sysblk.sys_reset = TRUE;
|
|
|
|
/* Set default SHCMDOPT to DISABLE NODIAG8 */
|
|
sysblk.shcmdopt = 0;
|
|
|
|
/* Save process ID */
|
|
sysblk.hercules_pid = getpid();
|
|
|
|
/* Save thread ID of main program */
|
|
sysblk.impltid = thread_id();
|
|
|
|
/* Save TOD of when we were first IMPL'ed */
|
|
time( &sysblk.impltime );
|
|
|
|
/* Set to LPAR mode with LPAR 1, LPAR ID of 01, and CPUIDFMT 0 */
|
|
sysblk.lparmode = 1; /* LPARNUM 1 # LPAR ID 01 */
|
|
sysblk.lparnum = 1; /* ... */
|
|
sysblk.cpuidfmt = 0; /* CPUIDFMT 0 */
|
|
sysblk.operation_mode = om_mif; /* Default to MIF operaitons */
|
|
|
|
/* set default CPU identifier */
|
|
sysblk.cpumodel = 0x0586;
|
|
sysblk.cpuversion = 0xFD;
|
|
sysblk.cpuserial = 0x000001;
|
|
sysblk.cpuid = createCpuId( sysblk.cpumodel, sysblk.cpuversion,
|
|
sysblk.cpuserial, 0 );
|
|
|
|
sysblk.panrate = PANEL_REFRESH_RATE_SLOW;
|
|
|
|
/* set default Program Interrupt Trace */
|
|
sysblk.pgminttr = OS_DEFAULT;
|
|
sysblk.ostailor = OSTAILOR_DEFAULT;
|
|
|
|
sysblk.timerint = DEF_TOD_UPDATE_USECS;
|
|
|
|
#if defined( _FEATURE_073_TRANSACT_EXEC_FACILITY )
|
|
sysblk.txf_timerint = sysblk.timerint;
|
|
#endif
|
|
|
|
#if defined( _FEATURE_ECPSVM )
|
|
sysblk.ecpsvm.available = 0;
|
|
sysblk.ecpsvm.level = 20;
|
|
#endif
|
|
|
|
#if defined( OPTION_SHARED_DEVICES )
|
|
sysblk.shrdport = 0;
|
|
#endif
|
|
|
|
/* setup defaults for CONFIG symbols */
|
|
{
|
|
char buf[8];
|
|
|
|
set_symbol( "LPARNAME", str_lparname());
|
|
set_symbol( "LPARNUM", "1" );
|
|
set_symbol( "CPUIDFMT", "0" );
|
|
|
|
MSGBUF( buf, "%06X", sysblk.cpuserial );
|
|
set_symbol( "CPUSERIAL", buf );
|
|
|
|
MSGBUF( buf, "%04X", sysblk.cpumodel );
|
|
set_symbol( "CPUMODEL", buf );
|
|
|
|
#if defined( _FEATURE_073_TRANSACT_EXEC_FACILITY )
|
|
defsym_TXF_models();
|
|
#endif
|
|
}
|
|
|
|
#if defined( _FEATURE_047_CMPSC_ENH_FACILITY )
|
|
sysblk.zpbits = DEF_CMPSC_ZP_BITS;
|
|
#endif
|
|
|
|
/* Initialize Trace File helper function pointers */
|
|
sysblk.s370_gsk = &s370_get_storage_key;
|
|
sysblk.s390_gsk = &s390_get_storage_key;
|
|
sysblk.z900_gsk = &z900_get_storage_key;
|
|
|
|
sysblk.s370_vtr = &s370_virt_to_real;
|
|
sysblk.s390_vtr = &s390_virt_to_real;
|
|
sysblk.z900_vtr = &z900_virt_to_real;
|
|
|
|
sysblk.s370_sit = &s370_store_int_timer;
|
|
sysblk.gct = &get_cpu_timer;
|
|
|
|
/* Initialize locks, conditions, and attributes */
|
|
initialize_lock( &sysblk.tracefileLock );
|
|
initialize_lock( &sysblk.bindlock );
|
|
initialize_lock( &sysblk.config );
|
|
initialize_lock( &sysblk.todlock );
|
|
initialize_lock( &sysblk.mainlock );
|
|
initialize_lock( &sysblk.intlock );
|
|
initialize_lock( &sysblk.iointqlk );
|
|
initialize_lock( &sysblk.sigplock );
|
|
initialize_lock( &sysblk.scrlock );
|
|
initialize_lock( &sysblk.crwlock );
|
|
initialize_lock( &sysblk.ioqlock );
|
|
initialize_lock( &sysblk.dasdcache_lock );
|
|
#if defined( _FEATURE_073_TRANSACT_EXEC_FACILITY )
|
|
initialize_lock( &sysblk.rublock );
|
|
#endif
|
|
|
|
initialize_condition( &sysblk.scrcond );
|
|
initialize_condition( &sysblk.ioqcond );
|
|
|
|
#if defined( OPTION_SHARED_DEVICES )
|
|
initialize_lock( &sysblk.shrdlock );
|
|
initialize_condition( &sysblk.shrdcond );
|
|
initialize_lock( &sysblk.shrdtracelock );
|
|
#endif
|
|
|
|
sysblk.mainowner = LOCK_OWNER_NONE;
|
|
sysblk.intowner = LOCK_OWNER_NONE;
|
|
|
|
/* Initialize thread creation attributes so all of hercules
|
|
can use them at any time when they need to create_thread
|
|
*/
|
|
initialize_detach_attr( DETACHED );
|
|
initialize_join_attr( JOINABLE );
|
|
|
|
/* Initialize CPU ENGINES locks and conditions */
|
|
initialize_condition( &sysblk.cpucond );
|
|
{
|
|
int i; char buf[32];
|
|
for (i=0; i < MAX_CPU_ENGS; i++)
|
|
{
|
|
MSGBUF( buf, "&sysblk.cpulock[%*d]", MAX_CPU_ENGS > 99 ? 3 : 2, i );
|
|
initialize_lock( &sysblk.cpulock[i] );
|
|
set_lock_name( &sysblk.cpulock[i], buf );
|
|
|
|
#if defined( _FEATURE_073_TRANSACT_EXEC_FACILITY )
|
|
MSGBUF( buf, "&sysblk.txf_lock[%*d]", MAX_CPU_ENGS > 99 ? 3 : 2, i );
|
|
initialize_lock( &sysblk.txf_lock[i] );
|
|
set_lock_name( &sysblk.txf_lock[i], buf );
|
|
#endif
|
|
}
|
|
}
|
|
initialize_condition( &sysblk.all_synced_cond );
|
|
initialize_condition( &sysblk.sync_done_cond );
|
|
|
|
/* Copy length for regs */
|
|
sysblk.regs_copy_len = (int)((uintptr_t)&sysblk.dummyregs.regs_copy_end
|
|
- (uintptr_t)&sysblk.dummyregs);
|
|
|
|
/* Set the NoUI_mode flag indicating whether we're running in
|
|
background/no-user-interface mode or not (meaning both stdout
|
|
and stderr are redirected to a non-tty device). Note that this
|
|
flag needs to be set BEFORE logger_init gets called since the
|
|
"logger_logfile_write()" function relies on its setting.
|
|
*/
|
|
if (!isatty(STDERR_FILENO) && !isatty(STDOUT_FILENO))
|
|
sysblk.NoUI_mode = 1; /* Leave -n intact */
|
|
|
|
/* Initialize panel colors */
|
|
set_panel_colors();
|
|
|
|
/* Initialize the logmsg pipe and associated logger thread.
|
|
This causes all subsequent logmsg's to be redirected to
|
|
the logger facility for handling by virtue of stdout/stderr
|
|
being redirected to the logger facility.
|
|
|
|
NOTE that the logger facility must ALWAYS be initialized
|
|
since other threads depend on it to be able to retrieve
|
|
log messages. This is true regardless of whether or not
|
|
the log messages are being redirected to a hardcopy file.
|
|
|
|
The HAO thread (Hercules Automatic Operator) for example
|
|
becomes essentially useless if it's unable to retrieve
|
|
log messages to see whether they match any of its rules.
|
|
*/
|
|
logger_init();
|
|
|
|
/* Setup the default codepage */
|
|
set_codepage( NULL );
|
|
|
|
/* Initialize default HDL modules load directory */
|
|
hdl_initpath( NULL );
|
|
|
|
/* Cap the default nice value to zero if setuid is not available */
|
|
#if !defined( _MSVC_ )
|
|
#if !defined( NO_SETUID )
|
|
if (sysblk.suid)
|
|
#endif
|
|
{
|
|
if (sysblk.hercnice < 0)
|
|
sysblk.hercnice = 0;
|
|
}
|
|
#endif
|
|
|
|
/* Initialize default thread priorities */
|
|
sysblk.cpuprio = DEFAULT_CPU_PRIO; /* (lowest) */
|
|
sysblk.devprio = DEFAULT_DEV_PRIO; /* | */
|
|
sysblk.srvprio = DEFAULT_SRV_PRIO; /* | */
|
|
sysblk.hercprio = DEFAULT_HERC_PRIO /* V */;
|
|
sysblk.todprio = DEFAULT_TOD_PRIO; /* (highest) */
|
|
|
|
#if defined( BUILD_APPLE_M1 )
|
|
/* Initialize default qos classes high to low */
|
|
sysblk.qos_user_interactive = QOS_CLASS_USER_INTERACTIVE;
|
|
sysblk.qos_user_initiated = QOS_CLASS_USER_INITIATED;
|
|
sysblk.qos_default = QOS_CLASS_DEFAULT;
|
|
sysblk.qos_utility = QOS_CLASS_UTILITY;
|
|
sysblk.qos_background = QOS_CLASS_BACKGROUND;
|
|
#endif
|
|
|
|
/* Set the priority of the main Hercules thread */
|
|
SET_THREAD_PRIORITY( sysblk.hercprio, sysblk.qos_user_initiated );
|
|
if (rc != 0)
|
|
{
|
|
#if defined( BUILD_APPLE_M1 )
|
|
// "Setting main thread QoS to USER_INITIATED failed: %s"
|
|
WRMSG( HHC00113, "E", strerror( rc ));
|
|
#else
|
|
// "set_thread_priority( %d ) failed: %s"
|
|
WRMSG( HHC00109, "E", sysblk.hercprio, strerror( rc ));
|
|
#endif
|
|
|
|
// "Defaulting all threads to priority %d"
|
|
WRMSG( HHC00110, "W", sysblk.minprio );
|
|
|
|
sysblk.cpuprio = sysblk.minprio; /* (lowest) */
|
|
sysblk.devprio = sysblk.minprio; /* | */
|
|
sysblk.srvprio = sysblk.minprio; /* | */
|
|
sysblk.hercprio = sysblk.minprio; /* V */
|
|
sysblk.todprio = sysblk.minprio; /* (highest) */
|
|
}
|
|
|
|
/* PROGRAMMING NOTE: we defer setting thread priorities until AFTER
|
|
the sysblk's default thread priority fields have been properly
|
|
initialized, just as we also defer logging any thread started
|
|
messages until after the logger has been initialized, since it
|
|
is the logger that processes all WRMSG() calls.
|
|
*/
|
|
|
|
/* Set the priority of the logger thread */
|
|
set_thread_priority_id(sysblk.loggertid, sysblk.srvprio);
|
|
|
|
/* Process command-line arguments. Exit if any serious errors. */
|
|
if ((rc = process_args( argc, argv )) != PROCESS_ARGS_OK)
|
|
{
|
|
switch (rc)
|
|
{
|
|
case PROCESS_ARGS_ERROR:
|
|
display_version(stdout, 0, NULL);
|
|
|
|
/* Show them our command line arguments */
|
|
arghelp();
|
|
|
|
// "Terminating due to %d argument errors"
|
|
WRMSG( HHC02343, "S", rc );
|
|
/* FALLTHROUGH */ // silence GCC warning
|
|
|
|
case PROCESS_ARGS_EXIT:
|
|
delayed_exit( rc );
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
/* The following comment is incorrect and misleading because
|
|
logger_init() had already been called above, before the
|
|
display_version() call. So we won't do it again here.
|
|
*/
|
|
|
|
/* Now display the version information again after logger_init
|
|
has been called so that either the panel display thread or the
|
|
external gui can see the version which was previously possibly
|
|
only displayed to the actual physical screen the first time we
|
|
did it further above (depending on whether we're running in
|
|
NoUI_mode (external gui mode) or not). This is the call that
|
|
the panel thread or external gui actually "sees".
|
|
|
|
The first call further above wasn't seen by either since it
|
|
was issued before logger_init was called and thus got written
|
|
directly to the physical screen whereas this one will be inter-
|
|
cepted and handled by the logger facility thereby allowing the
|
|
panel thread or external gui to "see" it and thus display it.
|
|
*/
|
|
|
|
/* Always show version right away */
|
|
display_version(stdout, 0, NULL);
|
|
display_build_options ( stdout, 0 );
|
|
display_extpkg_vers ( stdout, 0 );
|
|
|
|
/* We log these *after* the version number display. */
|
|
|
|
/* Log our own thread started message (better late than never) */
|
|
LOG_THREAD_BEGIN(IMPL_THREAD_NAME);
|
|
|
|
/* Log the logger thread */
|
|
LOG_TID_BEGIN(sysblk.loggertid, LOGGER_THREAD_NAME);
|
|
|
|
/* Warn if crash dumps aren't enabled */
|
|
#if !defined( _MSVC_ )
|
|
{
|
|
struct rlimit core_limit;
|
|
if (getrlimit( RLIMIT_CORE, &core_limit ) == 0)
|
|
sysblk.ulimit_unlimited = (RLIM_INFINITY == core_limit.rlim_cur);
|
|
if (!sysblk.ulimit_unlimited)
|
|
// "Crash dumps NOT enabled"
|
|
WRMSG( HHC00017, "W" );
|
|
}
|
|
#endif
|
|
|
|
/* Report whether Hercules is running in "elevated" mode or not */
|
|
// HHC00018 "Hercules is %srunning in elevated mode"
|
|
if (are_elevated())
|
|
WRMSG( HHC00018, "I", "" );
|
|
else
|
|
WRMSG( HHC00018, "W", "NOT " );
|
|
|
|
#if !defined(WIN32) && !defined(HAVE_STRERROR_R)
|
|
strerror_r_init();
|
|
#endif
|
|
|
|
#if defined(OPTION_SCSI_TAPE)
|
|
initialize_lock ( &sysblk.stape_lock );
|
|
initialize_condition ( &sysblk.stape_getstat_cond );
|
|
InitializeListHead ( &sysblk.stape_mount_link );
|
|
InitializeListHead ( &sysblk.stape_status_link );
|
|
#endif /* defined(OPTION_SCSI_TAPE) */
|
|
|
|
if (sysblk.scrtest)
|
|
{
|
|
// "Hercules is running in test mode."
|
|
WRMSG (HHC00019, "W" );
|
|
if (sysblk.scrfactor != 1.0)
|
|
// "Test timeout factor = %3.1f"
|
|
WRMSG( HHC00021, "I", sysblk.scrfactor );
|
|
}
|
|
|
|
/* Set default TCP keepalive values */
|
|
|
|
#if !defined( HAVE_BASIC_KEEPALIVE )
|
|
|
|
WARNING("TCP keepalive headers not found; check configure.ac")
|
|
WARNING("TCP keepalive support will NOT be generated")
|
|
// "This build of Hercules does not support TCP keepalive"
|
|
WRMSG( HHC02321, "E" );
|
|
|
|
#else // basic, partial or full: must attempt setting keepalive
|
|
|
|
#if !defined( HAVE_FULL_KEEPALIVE ) && !defined( HAVE_PARTIAL_KEEPALIVE )
|
|
|
|
WARNING("This build of Hercules will only have basic TCP keepalive support")
|
|
// "This build of Hercules has only basic TCP keepalive support"
|
|
WRMSG( HHC02322, "W" );
|
|
|
|
#elif !defined( HAVE_FULL_KEEPALIVE )
|
|
|
|
#if !defined( SUPPRESS_PARTIAL_KEEPALIVE_WARNING )
|
|
WARNING("This build of Hercules will only have partial TCP keepalive support")
|
|
#endif
|
|
|
|
// "This build of Hercules has only partial TCP keepalive support"
|
|
WRMSG( HHC02323, "W" );
|
|
|
|
#endif // (basic or partial)
|
|
|
|
/*
|
|
** Note: we need to try setting them to our desired values first
|
|
** and then retrieve the set values afterwards to detect systems
|
|
** which do not allow some values to be changed to ensure SYSBLK
|
|
** gets initialized with proper working default values.
|
|
*/
|
|
{
|
|
int rc, sfd, idle, intv, cnt;
|
|
|
|
/* Need temporary socket for setting/getting */
|
|
sfd = socket( AF_INET, SOCK_STREAM, 0 );
|
|
if (sfd < 0)
|
|
{
|
|
WRMSG( HHC02219, "E", "socket()", strerror( HSO_errno ));
|
|
idle = 0;
|
|
intv = 0;
|
|
cnt = 0;
|
|
}
|
|
else
|
|
{
|
|
idle = KEEPALIVE_IDLE_TIME;
|
|
intv = KEEPALIVE_PROBE_INTERVAL;
|
|
cnt = KEEPALIVE_PROBE_COUNT;
|
|
|
|
/* First, try setting the desired values */
|
|
rc = set_socket_keepalive( sfd, idle, intv, cnt );
|
|
|
|
if (rc < 0)
|
|
{
|
|
WRMSG( HHC02219, "E", "set_socket_keepalive()", strerror( HSO_errno ));
|
|
idle = 0;
|
|
intv = 0;
|
|
cnt = 0;
|
|
}
|
|
else
|
|
{
|
|
/* Report partial success */
|
|
if (rc > 0)
|
|
{
|
|
// "Not all TCP keepalive settings honored"
|
|
WRMSG( HHC02320, "W" );
|
|
}
|
|
|
|
sysblk.kaidle = idle;
|
|
sysblk.kaintv = intv;
|
|
sysblk.kacnt = cnt;
|
|
|
|
/* Retrieve current values from system */
|
|
if (get_socket_keepalive( sfd, &idle, &intv, &cnt ) < 0)
|
|
WRMSG( HHC02219, "E", "get_socket_keepalive()", strerror( HSO_errno ));
|
|
}
|
|
close_socket( sfd );
|
|
}
|
|
|
|
/* Initialize SYSBLK with default values */
|
|
sysblk.kaidle = idle;
|
|
sysblk.kaintv = intv;
|
|
sysblk.kacnt = cnt;
|
|
}
|
|
|
|
#endif // (KEEPALIVE)
|
|
|
|
/* Initialize runtime opcode tables */
|
|
init_runtime_opcode_tables();
|
|
|
|
/* Initialize the Hercules Dynamic Loader (HDL) */
|
|
rc = hdl_main
|
|
(
|
|
&the_real_panel_display,
|
|
&the_real_panel_command,
|
|
&the_real_replace_opcode,
|
|
&ckd_dasd_device_hndinfo,
|
|
&fba_dasd_device_hndinfo
|
|
);
|
|
|
|
if (rc != 0)
|
|
{
|
|
delayed_exit( rc );
|
|
return rc;
|
|
}
|
|
|
|
/* Load DYNGUI module if needed */
|
|
if (extgui)
|
|
{
|
|
if (hdl_loadmod( "dyngui", HDL_LOAD_NOUNLOAD ) != 0)
|
|
{
|
|
USLEEP( 10000 ); // (give logger time to show them the error message)
|
|
// "Load of dyngui.dll failed, hercules terminated"
|
|
WRMSG( HHC01409, "S" );
|
|
delayed_exit( -1 );
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* Load modules specified via command-line -l/--ldmod option */
|
|
if (modcount)
|
|
{
|
|
int modnum;
|
|
bool err = false;
|
|
|
|
for (modnum = 0; modnum < modcount; modnum++)
|
|
{
|
|
if (hdl_loadmod( modnames[ modnum ], HDL_LOAD_DEFAULT) != 0)
|
|
err = true;
|
|
|
|
free( modnames[ modnum ]);
|
|
modnames[ modnum ] = NULL;
|
|
}
|
|
|
|
modcount = 0; // (reset to zero now that they're all loaded)
|
|
|
|
if (err)
|
|
{
|
|
USLEEP( 10000 ); // (give logger time to display message)
|
|
// "Hercules terminating, see previous messages for reason"
|
|
WRMSG( HHC01408, "S");
|
|
delayed_exit( -1 );
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* The SIGINT signal is sent to a process by its controlling
|
|
terminal when a user wishes to interrupt the process. This
|
|
is typically initiated by pressing Ctrl+C, but on some
|
|
systems, the "delete" character or "break" key can be used.
|
|
|
|
The SIGTERM signal is sent to a process to request its
|
|
termination. Unlike the SIGKILL signal, it can be caught
|
|
and interpreted or ignored by the process. This allows
|
|
the process to perform nice termination releasing resources
|
|
and saving state if appropriate. SIGINT is nearly identical
|
|
to SIGTERM.
|
|
*/
|
|
|
|
/* Register the SIGINT handler */
|
|
if (signal( SIGINT, sigint_handler ) == SIG_ERR)
|
|
{
|
|
// "Cannot register %s handler: %s"
|
|
WRMSG( HHC01410, "S", "SIGINT", strerror( errno ));
|
|
delayed_exit( -1 );
|
|
return 1;
|
|
}
|
|
|
|
/* Register the SIGTERM handler */
|
|
if (signal( SIGTERM, sigterm_handler ) == SIG_ERR)
|
|
{
|
|
// "Cannot register %s handler: %s"
|
|
WRMSG( HHC01410, "S", "SIGTERM", strerror( errno ));
|
|
delayed_exit( -1 );
|
|
return 1;
|
|
}
|
|
|
|
#if defined( _MSVC_ )
|
|
/* Register the Window console ctrl handlers */
|
|
if (!IsDebuggerPresent())
|
|
{
|
|
TID dummy;
|
|
BOOL bSuccess = FALSE;
|
|
|
|
/* The Windows the console_ctrl_handler function
|
|
accomplishes the same thing as what a SIGINT or
|
|
SIGTERM signal handler does on Unix systems: it
|
|
handles Ctrl+C and Close events.
|
|
*/
|
|
if (!SetConsoleCtrlHandler( console_ctrl_handler, TRUE ))
|
|
{
|
|
// "Cannot register %s handler: %s"
|
|
WRMSG( HHC01410, "S", "Console-ctrl", strerror( errno ));
|
|
delayed_exit(-1);
|
|
return 1;
|
|
}
|
|
|
|
g_hWndEvt = CreateEvent( NULL, TRUE, FALSE, NULL );
|
|
|
|
if ((rc = create_thread( &dummy, DETACHED,
|
|
WinMsgThread, (void*) &bSuccess, "WinMsgThread" )) != 0)
|
|
{
|
|
// "Error in function create_thread(): %s"
|
|
WRMSG( HHC00102, "E", strerror( rc ));
|
|
CloseHandle( g_hWndEvt );
|
|
delayed_exit(-1);
|
|
return 1;
|
|
}
|
|
|
|
// (wait for thread to create window)
|
|
WaitForSingleObject( g_hWndEvt, INFINITE );
|
|
CloseHandle( g_hWndEvt );
|
|
|
|
// Was message window successfully created?
|
|
|
|
if (!g_hMsgWnd)
|
|
{
|
|
// "Error in function %s: %s"
|
|
WRMSG( HHC00136, "E", "WinMsgThread", "CreateWindowEx() failed");
|
|
delayed_exit(-1);
|
|
return 1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined( HAVE_DECL_SIGPIPE ) && HAVE_DECL_SIGPIPE
|
|
/* Ignore the SIGPIPE signal, otherwise Hercules may terminate with
|
|
Broken Pipe error if the printer driver writes to a closed pipe */
|
|
if (signal( SIGPIPE, SIG_IGN ) == SIG_ERR)
|
|
{
|
|
// "Cannot suppress SIGPIPE signal: %s"
|
|
WRMSG( HHC01411, "E", strerror( errno ));
|
|
}
|
|
#endif
|
|
|
|
{
|
|
int fds[2];
|
|
initialize_lock(&sysblk.cnslpipe_lock);
|
|
initialize_lock(&sysblk.sockpipe_lock);
|
|
sysblk.cnslpipe_flag=0;
|
|
sysblk.sockpipe_flag=0;
|
|
VERIFY( create_pipe(fds) >= 0 );
|
|
sysblk.cnslwpipe=fds[1];
|
|
sysblk.cnslrpipe=fds[0];
|
|
VERIFY( create_pipe(fds) >= 0 );
|
|
sysblk.sockwpipe=fds[1];
|
|
sysblk.sockrpipe=fds[0];
|
|
}
|
|
|
|
#ifdef HAVE_REXX
|
|
/* Initialize Rexx */
|
|
InitializeRexx();
|
|
#endif
|
|
|
|
/* System initialisation time */
|
|
sysblk.todstart = hw_clock() << 8;
|
|
|
|
hdl_addshut( "release_config", release_config, NULL );
|
|
|
|
#if defined( OPTION_WATCHDOG )
|
|
/* Start the watchdog thread */
|
|
rc = create_thread( &sysblk.wdtid, DETACHED,
|
|
watchdog_thread, NULL, WATCHDOG_THREAD_NAME );
|
|
if (rc)
|
|
{
|
|
// "Error in function create_thread(): %s"
|
|
WRMSG( HHC00102, "E", strerror( rc ));
|
|
delayed_exit( -1 );
|
|
return 1;
|
|
}
|
|
#endif /* defined( OPTION_WATCHDOG ) */
|
|
|
|
/* Build system configuration */
|
|
if ( build_config (cfgorrc[want_cfg].filename) )
|
|
{
|
|
delayed_exit(-1);
|
|
return 1;
|
|
}
|
|
|
|
sysblk.config_processed = true;
|
|
sysblk.cfg_timerint = sysblk.timerint;
|
|
|
|
/* Report CCKD dasd image garbage states at startup */
|
|
cckd_gc_rpt_states();
|
|
|
|
#if defined( _FEATURE_073_TRANSACT_EXEC_FACILITY )
|
|
|
|
if (FACILITY_ENABLED_ARCH( 073_TRANSACT_EXEC, ARCH_900_IDX ))
|
|
{
|
|
txf_model_warning( true );
|
|
txf_set_timerint( true );
|
|
}
|
|
else
|
|
txf_set_timerint( false );
|
|
|
|
#endif /* defined( _FEATURE_073_TRANSACT_EXEC_FACILITY ) */
|
|
|
|
/* Check if various host instructions are available or not */
|
|
check_host_instruction_availability();
|
|
|
|
/* Process the .rc file synchronously when in No UI mode. */
|
|
/* Otherwise Start up the RC file processing thread. */
|
|
if (sysblk.NoUI_mode)
|
|
process_rc_file( NULL );
|
|
else
|
|
{
|
|
rc = create_thread( &rctid, DETACHED,
|
|
process_rc_file, NULL, "process_rc_file" );
|
|
if (rc)
|
|
WRMSG( HHC00102, "E", strerror( rc ));
|
|
}
|
|
|
|
if (log_callback)
|
|
{
|
|
// 'herclin' called us. IT iS in charge. Create its requested
|
|
// logmsg intercept callback function and return back to it.
|
|
rc = create_thread( &logcbtid, DETACHED,
|
|
log_do_callback, NULL, "log_do_callback" );
|
|
if (rc)
|
|
// "Error in function create_thread(): %s"
|
|
WRMSG( HHC00102, "E", strerror( rc ));
|
|
|
|
return (rc);
|
|
}
|
|
|
|
/* Activate the control panel */
|
|
if (!sysblk.NoUI_mode)
|
|
panel_display(); /* Returns only AFTER Hercules is shutdown */
|
|
else
|
|
{
|
|
/*-----------------------------------------------*/
|
|
/* We're in No UI mode... */
|
|
/*-----------------------------------------------*/
|
|
|
|
if (noui_task) /* External GUI in control? */
|
|
noui_task(); /* Returns only when GUI decides to */
|
|
else
|
|
{
|
|
/* No-User-Interface mode without any external GUI... */
|
|
|
|
process_script_file( "-", true );
|
|
|
|
/* We come here only if the user did ctl-d on a tty,
|
|
or we reached EOF on stdin. No quit command has
|
|
been issued (yet) since that (via do_shutdown())
|
|
would not return. So we issue the quit command
|
|
here once all CPUs have quiesced, since with no
|
|
CPUs doing anything and stdin at EOF, there's no
|
|
longer any reason for us to stick around!
|
|
|
|
UNLESS, of course, there were never at any CPUs
|
|
defined/configured to begin with! (i.e. "NUMCPU 0"
|
|
was specified, such as would be the case if Hercules
|
|
was running solely as a Shared Device Server with
|
|
no guest operating IPLed or running for example).
|
|
|
|
In such a case we must continue running in order
|
|
to continue serving Shared Dasd I/O requests for
|
|
our Shared Device clients. So we continue running
|
|
FOREVER. (Yes, *FOREVER!*) We NEVER exit. We can
|
|
only go away (disappear) when the user wants us to,
|
|
by either explicitly KILLING the Hercules process
|
|
itself, or by issuing the 'quit' command via e.g.
|
|
our own builtin HTTP Server interface.
|
|
*/
|
|
while (0
|
|
|| sysblk.started_mask /* CPU(s) still running? */
|
|
|| !sysblk.config_mask /* ZERO CPUs configured? */
|
|
)
|
|
USLEEP( 10 * 1000 ); /* Wait on CPU(s) or forever */
|
|
|
|
if (sysblk.config_mask) /* If NUMCPU > 0, then do */
|
|
quit_cmd( 0, 0, 0); /* normal/clean shutdown */
|
|
}
|
|
}
|
|
|
|
/*
|
|
** PROGRAMMING NOTE: the following code is only ever reached
|
|
** if Hercules is run in normal panel mode -OR- when it is run
|
|
** under the control of an external GUI (i.e. noui_task).
|
|
*/
|
|
ASSERT( sysblk.shutdown ); // (why else would we be here?!)
|
|
|
|
#ifdef _MSVC_
|
|
SetConsoleCtrlHandler( console_ctrl_handler, FALSE );
|
|
socket_deinit();
|
|
#endif
|
|
fflush( stdout );
|
|
USLEEP( 10000 );
|
|
return 0; /* return back to bootstrap.c */
|
|
} /* end function impl */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Initialize program name */
|
|
/*-------------------------------------------------------------------*/
|
|
static void init_progname( int argc, char* argv[] )
|
|
{
|
|
free( sysblk.hercules_pgmname );
|
|
free( sysblk.hercules_pgmpath );
|
|
|
|
if (argc < 1 || !strlen( argv[0] ))
|
|
{
|
|
sysblk.hercules_pgmname = strdup( "hercules" );
|
|
sysblk.hercules_pgmpath = strdup( "" );
|
|
}
|
|
else
|
|
{
|
|
char path[ MAX_PATH ];
|
|
|
|
#if defined( _MSVC_ )
|
|
GetModuleFileName( NULL, path, _countof( path ));
|
|
#else
|
|
STRLCPY( path, argv[0] );
|
|
#endif
|
|
sysblk.hercules_pgmname = strdup( basename( path ));
|
|
sysblk.hercules_pgmpath = strdup( dirname( path ));
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* process_args - Process command-line arguments */
|
|
/*-------------------------------------------------------------------*/
|
|
static int process_args( int argc, char* argv[] )
|
|
{
|
|
int arg_error = PROCESS_ARGS_OK;
|
|
int c = 0; /* Next option flag */
|
|
|
|
// Save a copy of the command line before getopt mangles argv[]
|
|
|
|
if (argc > 0)
|
|
{
|
|
int i;
|
|
size_t len;
|
|
|
|
for (len=0, i=0; i < argc; i++ )
|
|
len += strlen( argv[i] ) + 1;
|
|
|
|
sysblk.hercules_cmdline = malloc( len );
|
|
strlcpy( sysblk.hercules_cmdline, argv[0], len );
|
|
|
|
for (i=1; i < argc; i++)
|
|
{
|
|
strlcat( sysblk.hercules_cmdline, " ", len );
|
|
strlcat( sysblk.hercules_cmdline, argv[i], len );
|
|
}
|
|
}
|
|
|
|
opterr = 0; /* We'll print our own error messages thankyouverymuch */
|
|
|
|
if (2 <= argc && !strcmp(argv[1], "-?"))
|
|
{
|
|
arg_error = PROCESS_ARGS_ERROR;
|
|
goto error;
|
|
}
|
|
|
|
for (; EOF != c ;)
|
|
{
|
|
c = /* Work area for getopt */
|
|
|
|
#if defined( HAVE_GETOPT_LONG )
|
|
getopt_long( argc, argv, shortopts, longopts, NULL );
|
|
#else
|
|
getopt( argc, argv, shortopts );
|
|
#endif
|
|
|
|
switch (c)
|
|
{
|
|
case EOF:
|
|
|
|
break; /* (we're done) */
|
|
|
|
case 0: /* getopt_long() set a variable; keep going */
|
|
|
|
break;
|
|
|
|
case 'V':
|
|
display_version(stdout, 0, NULL);
|
|
arg_error = PROCESS_ARGS_EXIT;
|
|
break;
|
|
|
|
case 'h': /* -h[=type] or --help[=type] */
|
|
|
|
if (optarg) /* help type specified? */
|
|
{
|
|
if (0
|
|
|| strcasecmp( optarg, "short" ) == 0
|
|
)
|
|
{
|
|
arg_error = PROCESS_ARGS_ERROR; // (forced by help option)
|
|
; // (do nothing)
|
|
}
|
|
else if (0
|
|
|| strcasecmp( optarg, "version" ) == 0
|
|
)
|
|
{
|
|
display_version( stdout, 0, NULL );
|
|
arg_error = PROCESS_ARGS_EXIT;
|
|
}
|
|
else if (0
|
|
|| strcasecmp( optarg, "build" ) == 0
|
|
)
|
|
{
|
|
display_build_options( stdout, 0 );
|
|
arg_error = PROCESS_ARGS_EXIT;
|
|
}
|
|
else if (0
|
|
|| strcasecmp( optarg, "all" ) == 0
|
|
|| strcasecmp( optarg, "long" ) == 0
|
|
|| strcasecmp( optarg, "full" ) == 0
|
|
)
|
|
{
|
|
display_version ( stdout, 0, NULL );
|
|
display_build_options( stdout, 0 );
|
|
display_extpkg_vers ( stdout, 0 );
|
|
arg_error = PROCESS_ARGS_EXIT;
|
|
}
|
|
else
|
|
{
|
|
// "Invalid help option argument: \"%s\""
|
|
WRMSG( HHC00025, "E", optarg );
|
|
arg_error = PROCESS_ARGS_ERROR; // (forced by help option)
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Show them our command line arguments */
|
|
display_version(stdout, 0, NULL);
|
|
arghelp();
|
|
arg_error = PROCESS_ARGS_EXIT;
|
|
}
|
|
|
|
break;
|
|
|
|
case 'f':
|
|
|
|
cfgorrc[ want_cfg ].filename = optarg;
|
|
break;
|
|
|
|
case 'o':
|
|
|
|
log_sethrdcpy( optarg );
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
cfgorrc[ want_rc ].filename = optarg;
|
|
break;
|
|
|
|
case 's':
|
|
{
|
|
char* sym = NULL;
|
|
char* value = NULL;
|
|
char* strtok_str = NULL;
|
|
|
|
if (strlen( optarg ) >= 3)
|
|
{
|
|
sym = strtok_r( optarg, "=", &strtok_str);
|
|
value = strtok_r( NULL, "=", &strtok_str);
|
|
|
|
if (sym && value)
|
|
{
|
|
int j;
|
|
|
|
for (j=0; j < (int) strlen( sym ); j++)
|
|
{
|
|
if (islower( (unsigned char)sym[j] ))
|
|
sym[j] = toupper( (unsigned char)sym[j] );
|
|
}
|
|
|
|
set_symbol( sym, value );
|
|
}
|
|
else
|
|
// "Symbol and/or Value is invalid; ignored"
|
|
WRMSG( HHC01419, "E" );
|
|
}
|
|
else
|
|
// "Symbol and/or Value is invalid; ignored"
|
|
WRMSG( HHC01419, "E" );
|
|
}
|
|
break;
|
|
|
|
case 'p':
|
|
|
|
hdl_initpath( optarg );
|
|
break;
|
|
|
|
case 'l':
|
|
{
|
|
char *dllname, *strtok_str = NULL;
|
|
|
|
for(dllname = strtok_r(optarg,", ",&strtok_str);
|
|
dllname;
|
|
dllname = strtok_r(NULL,", ",&strtok_str))
|
|
{
|
|
if (modcount < MAX_MODS)
|
|
modnames[ modcount++ ] = strdup( dllname ); // (caller will free)
|
|
else
|
|
{
|
|
// "Startup parm -l: maximum loadable modules %d exceeded; remainder not loaded"
|
|
WRMSG( HHC01406, "W", MAX_MODS );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'b':
|
|
|
|
sysblk.logofile = optarg;
|
|
break;
|
|
|
|
case 'v':
|
|
|
|
sysblk.msglvl |= MLVL_VERBOSE;
|
|
break;
|
|
|
|
case 'd': // (deprecated)
|
|
{
|
|
bool dasddash =
|
|
(1
|
|
&& argv[ optind-1 ][0] == '-'
|
|
&& argv[ optind-1 ][1] == '-'
|
|
);
|
|
|
|
// "Option \"%s\" has been deprecated; use \"%s\" instead"
|
|
WRMSG( HHC00022, "W", argv[ optind-1 ], dasddash ? "--NoUI" : "-n" );
|
|
|
|
sysblk.NoUI_mode = 1;
|
|
break;
|
|
}
|
|
|
|
case 'n':
|
|
|
|
sysblk.NoUI_mode = 1;
|
|
break;
|
|
|
|
case 'e':
|
|
|
|
extgui = 1;
|
|
break;
|
|
|
|
case 't':
|
|
|
|
sysblk.scrtest = 1;
|
|
sysblk.scrfactor = 1.0;
|
|
|
|
if (optarg)
|
|
{
|
|
double scrfactor;
|
|
double max_factor = MAX_RUNTEST_FACTOR;
|
|
|
|
/* Round down to nearest 10th of a second */
|
|
max_factor = floor( max_factor * 10.0 ) / 10.0;
|
|
scrfactor = atof( optarg );
|
|
|
|
if (scrfactor >= 1.0 && scrfactor <= max_factor)
|
|
sysblk.scrfactor = scrfactor;
|
|
else
|
|
{
|
|
// "Test timeout factor %s outside of valid range 1.0 to %3.1f"
|
|
WRMSG( HHC00020, "S", optarg, max_factor );
|
|
arg_error = PROCESS_ARGS_ERROR;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
char buf[16];
|
|
|
|
if (isprint( (unsigned char)optopt ))
|
|
MSGBUF( buf, "'-%c'", optopt );
|
|
else
|
|
MSGBUF( buf, "(hex %2.2x)", optopt );
|
|
|
|
// "Invalid/unsupported option: \"%s\""
|
|
WRMSG( HHC00023, "S", buf );
|
|
arg_error = PROCESS_ARGS_ERROR;
|
|
}
|
|
break;
|
|
|
|
} // end switch(c)
|
|
} // end while
|
|
|
|
while (optind < argc)
|
|
{
|
|
// "Unrecognized option: \"%s\""
|
|
WRMSG( HHC00024, "S", argv[ optind++ ]);
|
|
arg_error = PROCESS_ARGS_ERROR;
|
|
}
|
|
|
|
error:
|
|
|
|
/* Terminate if invalid arguments were detected */
|
|
if (arg_error != PROCESS_ARGS_OK)
|
|
{
|
|
/* Do nothing. Caller will call "arghelp" to
|
|
show them our command-line arguments... */
|
|
}
|
|
else /* Check for config and rc file, but don't open */
|
|
{
|
|
struct stat st;
|
|
int i, rv;
|
|
bool using_default;
|
|
|
|
for (i=0; cfgorrccount > i; i++)
|
|
{
|
|
using_default = false;
|
|
|
|
/* If no value explicitly specified, try environment */
|
|
if (0
|
|
|| !cfgorrc[i].filename
|
|
|| !cfgorrc[i].filename[0]
|
|
)
|
|
{
|
|
const char* envname = get_symbol( cfgorrc[i].envname );
|
|
|
|
if (envname && envname[0])
|
|
cfgorrc[i].filename = envname;
|
|
else
|
|
{
|
|
/* If no environment, use our hard coded default */
|
|
using_default = true;
|
|
cfgorrc[i].filename = cfgorrc[i].defaultfile;
|
|
}
|
|
}
|
|
|
|
/* Explicit request for no file use at all? */
|
|
if (strcasecmp( cfgorrc[i].filename, "none" ) == 0)
|
|
{
|
|
cfgorrc[i].filename = NULL; /* Suppress file */
|
|
continue;
|
|
}
|
|
|
|
/* File specified either explicitly, by environment,
|
|
or by hard coded default: verify its existence.
|
|
*/
|
|
if ((rv = stat( cfgorrc[i].filename, &st )) != 0)
|
|
{
|
|
/* If this is the .rc file, default to none */
|
|
if (want_rc == i && using_default)
|
|
{
|
|
cfgorrc[i].filename = NULL; /* Suppress file */
|
|
continue;
|
|
}
|
|
|
|
// "%s file '%s' not found: %s"
|
|
WRMSG( HHC02342, "S", cfgorrc[i].whatfile,
|
|
cfgorrc[i].filename, strerror( errno ));
|
|
arg_error = PROCESS_ARGS_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
fflush( stderr );
|
|
fflush( stdout );
|
|
|
|
return arg_error;
|
|
}
|