Files
org-hyperion-cules/impl.c
Fish (David B. Trout) a2e06edcc9 Fixes for unreliable console keepalive issue #44:
1. Fix configure.ac tcp.h header and keepalive constant checks and report during ./configure the keepalive support that was detected (none, basic or full).

2. #include the correct tcp.h header in hstdinc.h

3. Issue warning message at build time (during make) when full keepalive support is missing or partial.

4. Report partial or missing keepalive build support via version.c "With" and "Without" messages.

5. Issue error or warning in both impl.c as well as in 'conkpalv' command/statement processing function when keepalive support is either missing or only partially supported. Same for the 2703 device 'ka=' option too.

6. Enhance set_socket_keepalive function to let the caller know not only about failures but also about partial-only successes (so they can be reported).

7. Fix the bad setsockopt call in the Linux version of the set_socket_keepalive function that was using the wrong value, which was at the heart of the problem. (Note: setsockopt was returning "success" anyway even though it had actually failed! [to accomplish anything].)

8. Introduce new get_socket_keepalive function to allow retrieval and reporting of current keepalive settings for a given socket.

9. Manually maintain socket keepalive values for Windows builds so it too can support retrieval of current values via new get_socket_keepalive function. (see comments in w32util.c "get_socket_keepalive" function!)

10. Minor HTML documentation edits.
2015-01-19 13:35:29 -08:00

1279 lines
39 KiB
C

/* IMPL.C (c) Copyright Roger Bowler, 1999-2012 */
/* 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"
#ifndef _IMPL_C_
#define _IMPL_C_
#endif
#ifndef _HENGINE_DLL_
#define _HENGINE_DLL_
#endif
#include "hercules.h"
#include "opcode.h"
#include "devtype.h"
#include "herc_getopt.h"
#include "hostinfo.h"
#include "history.h"
/* forward define process_script_file (ISW20030220-3) */
int process_script_file(char *,int);
static LOGCALLBACK log_callback=NULL;
/*-------------------------------------------------------------------*/
/* Register a LOG callback */
/*-------------------------------------------------------------------*/
DLL_EXPORT void registerLogCallback(LOGCALLBACK lcb)
{
log_callback=lcb;
}
/*-------------------------------------------------------------------*/
/* 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;
}
/*-------------------------------------------------------------------*/
/* Signal handler for SIGINT signal */
/*-------------------------------------------------------------------*/
static void sigint_handler (int signo)
{
// logmsg ("impl.c: sigint handler entered for thread %lu\n",/*debug*/
// thread_id()); /*debug*/
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();
delayed_exit(1);
}
/* Set SIGINT request pending flag */
sysblk.sigintreq = 1;
/* Activate instruction stepping */
sysblk.inststep = 1;
SET_IC_TRACE;
return;
} /* end function sigint_handler */
/*-------------------------------------------------------------------*/
/* Signal handler for SIGTERM signal */
/*-------------------------------------------------------------------*/
static void sigterm_handler (int signo)
{
// logmsg ("impl.c: sigterm handler entered for thread %lu\n",/*debug*/
// thread_id()); /*debug*/
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();
return;
} /* end function sigterm_handler */
#if defined( _MSVC_ )
/*-------------------------------------------------------------------*/
/* Signal handler for Windows signals */
/*-------------------------------------------------------------------*/
static BOOL WINAPI console_ctrl_handler (DWORD signo)
{
int i;
SetConsoleCtrlHandler(console_ctrl_handler, FALSE); // turn handler off while processing
switch ( signo )
{
case CTRL_BREAK_EVENT:
WRMSG (HHC01400, "I");
OBTAIN_INTLOCK(NULL);
ON_IC_INTKEY;
/* Signal waiting CPUs that an interrupt is pending */
WAKEUP_CPUS_MASK (sysblk.waiting_mask);
RELEASE_INTLOCK(NULL);
SetConsoleCtrlHandler(console_ctrl_handler, TRUE); // reset handler
return TRUE;
break;
case CTRL_C_EVENT:
WRMSG(HHC01401, "I");
SetConsoleCtrlHandler(console_ctrl_handler, TRUE); // reset handler
return TRUE;
break;
case CTRL_CLOSE_EVENT:
case CTRL_SHUTDOWN_EVENT:
case CTRL_LOGOFF_EVENT:
if ( !sysblk.shutdown ) // (system shutdown not initiated)
{
WRMSG(HHC01402, "I", ( signo == CTRL_CLOSE_EVENT ? "close" :
signo == CTRL_SHUTDOWN_EVENT ? "shutdown" : "logoff" ),
( signo == CTRL_CLOSE_EVENT ? "immediate " : "" ) );
if ( signo == CTRL_CLOSE_EVENT )
sysblk.shutimmed = TRUE;
do_shutdown();
// logmsg("%s(%d): return from shutdown\n", __FILE__, __LINE__ ); /* debug */
for ( i = 0; i < 120; i++ )
{
if ( sysblk.shutdown && sysblk.shutfini )
{
// logmsg("%s(%d): %d shutdown completed\n", /* debug */
// __FILE__, __LINE__, i ); /* debug */
break;
}
else
{
// logmsg("%s(%d): %d waiting for shutdown to complete\n", /* debug */
// __FILE__, __LINE__, i ); /* debug */
sleep(1);
}
}
if ( !sysblk.shutfini )
{
sysblk.shutimmed = TRUE;
do_shutdown();
}
}
else
{
sysblk.shutimmed = TRUE;
do_shutdown();
WRMSG(HHC01403, "W", ( signo == CTRL_CLOSE_EVENT ? "close" :
signo == CTRL_SHUTDOWN_EVENT ? "shutdown" : "logoff" ) );
}
return TRUE;
break;
default:
return FALSE;
}
} /* end function console_ctrl_handler */
#endif
#if !defined(NO_SIGABEND_HANDLER)
static void *watchdog_thread(void *arg)
{
S64 savecount[MAX_CPU_ENGINES];
int i;
UNREFERENCED(arg);
/* Set watchdog priority just below cpu priority
such that it will not invalidly detect an
inoperable cpu */
if(sysblk.cpuprio >= 0)
set_thread_priority(0, sysblk.cpuprio+1);
for (i = 0; i < sysblk.maxcpu; i ++) savecount[i] = -1;
while(!sysblk.shutdown)
{
for (i = 0; i < sysblk.maxcpu; i++)
{
// obtain_lock (&sysblk.cpulock[i]);
if (IS_CPU_ONLINE(i)
&& sysblk.regs[i]->cpustate == CPUSTATE_STARTED
&& (!WAITSTATE(&sysblk.regs[i]->psw)
#if defined(_FEATURE_WAITSTATE_ASSIST)
&& !(sysblk.regs[i]->sie_active && WAITSTATE(&sysblk.regs[i]->guestregs->psw))
#endif
))
{
/* If the cpu is running but not executing
instructions then it must be malfunctioning */
if((INSTCOUNT(sysblk.regs[i]) == (U64)savecount[i])
&& !HDC1(debug_watchdog_signal, sysblk.regs[i]) )
{
/* Send signal to looping CPU */
signal_thread(sysblk.cputid[i], SIGUSR1);
savecount[i] = -1;
}
else
/* Save current instcount */
savecount[i] = INSTCOUNT(sysblk.regs[i]);
}
else
/* mark savecount invalid as CPU not in running state */
savecount[i] = -1;
// release_lock (&sysblk.cpulock[i]);
}
/* Sleep for 20 seconds */
SLEEP(20);
}
return NULL;
}
#endif /*!defined(NO_SIGABEND_HANDLER)*/
void *log_do_callback(void *dummy)
{
char *msgbuf;
int msgcnt = -1,msgnum;
UNREFERENCED(dummy);
while(msgcnt)
{
if((msgcnt = log_read(&msgbuf, &msgnum, LOG_BLOCK)))
{
log_callback(msgbuf,msgcnt);
}
}
return(NULL);
}
DLL_EXPORT COMMANDHANDLER getCommandHandler(void)
{
return(panel_command);
}
/*-------------------------------------------------------------------*/
/* Process .RC file thread */
/*-------------------------------------------------------------------*/
static char *rcname = NULL; /* hercules.rc name pointer */
static void* process_rc_file (void* dummy)
{
int is_default_rc = 0; /* 1 == default name used */
char pathname[MAX_PATH]; /* (work) */
UNREFERENCED(dummy);
/* Obtain the name of the hercules.rc file or default */
if (!rcname)
{
if (!(rcname = getenv("HERCULES_RC")))
{
rcname = "hercules.rc";
is_default_rc = 1;
}
}
if(!strcasecmp(rcname,"None"))
return NULL;
hostpath(pathname, rcname, sizeof(pathname));
/* Wait for panel thread to engage */
// ZZ FIXME:THIS NEED TO GO
while (!sysblk.panel_init)
usleep( 10 * 1000 );
/* Run the script processor for this file */
if (process_script_file(pathname,1) != 0)
if (ENOENT == errno)
if (!is_default_rc)
WRMSG(HHC01405, "E", pathname);
// (else error message already issued)
return NULL;
}
/*-------------------------------------------------------------------*/
/* IMPL main entry point */
/*-------------------------------------------------------------------*/
DLL_EXPORT int impl(int argc, char *argv[])
{
char *cfgfile; /* -> Configuration filename */
char pathname[MAX_PATH]; /* work area for filenames */
#if defined ( OPTION_LOCK_CONFIG_FILE )
int fd_cfg = -1; /* fd for config file */
#if !defined ( _MSVC_ )
struct flock fl_cfg; /* file lock for conf file */
#endif
#endif
int c; /* Work area for getopt */
int arg_error = 0; /* 1=Invalid arguments */
char *msgbuf; /* */
int msgnum; /* */
int msgcnt; /* */
TID rctid; /* RC file thread identifier */
TID logcbtid; /* RC file thread identifier */
int rc;
#if defined(EXTERNALGUI)
int e_gui = FALSE; /* EXTERNALGUI parm */
#endif
#if defined(OPTION_DYNAMIC_LOAD)
#define MAX_DLL_TO_LOAD 50
char *dll_load[MAX_DLL_TO_LOAD]; /* Pointers to modnames */
int dll_count; /* index into array */
#endif
/* Seed the pseudo-random number generator */
srand( time(NULL) );
/* Clear the system configuration block */
memset( &sysblk, 0, sizeof( SYSBLK ) );
VERIFY( MLOCK( &sysblk, sizeof( SYSBLK )) == 0);
#if defined (_MSVC_)
_setmaxstdio(2048);
#endif
/* Initialize EYE-CATCHERS for SYSBLK */
memset(&sysblk.blknam,SPACE,sizeof(sysblk.blknam));
memset(&sysblk.blkver,SPACE,sizeof(sysblk.blkver));
memset(&sysblk.blkend,SPACE,sizeof(sysblk.blkend));
sysblk.blkloc = swap_byte_U64((U64)((uintptr_t)&sysblk));
memcpy(sysblk.blknam,HDL_NAME_SYSBLK,strlen(HDL_NAME_SYSBLK));
memcpy(sysblk.blkver,HDL_VERS_SYSBLK,strlen(HDL_VERS_SYSBLK));
sysblk.blksiz = swap_byte_U32((U32)sizeof(SYSBLK));
{
char buf[32];
MSGBUF( buf, "END%13.13s", HDL_NAME_SYSBLK );
memcpy(sysblk.blkend, buf, sizeof(sysblk.blkend));
}
/* Initialize SETMODE and set user authority */
SETMODE(INIT);
#if defined(OPTION_DYNAMIC_LOAD)
for ( dll_count = 0; dll_count < MAX_DLL_TO_LOAD; dll_count++ )
dll_load[dll_count] = NULL;
dll_count = -1;
#endif
SET_THREAD_NAME("impl");
/* Initialize 'hostinfo' BEFORE display_version is called */
init_hostinfo( &hostinfo );
#ifdef _MSVC_
/* Initialize sockets package */
VERIFY( socket_init() == 0 );
#endif
/* Ensure hdl_shut is called in case of shutdown
hdl_shut will ensure entries are only called once */
atexit(hdl_shut);
if ( argc > 0 )
{
int i,len;
for (len = 0, i = 0; i < argc; i++ )
len += (int)strlen( (char *)argv[i] ) + 1;
sysblk.hercules_cmdline = (char *)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 );
}
}
/* Set program name */
if ( argc > 0 )
{
if ( strlen(argv[0]) == 0 )
{
sysblk.hercules_pgmname = strdup("hercules");
sysblk.hercules_pgmpath = strdup("");
}
else
{
char path[MAX_PATH];
#if defined( _MSVC_ )
GetModuleFileName( NULL, path, MAX_PATH );
#else
strncpy(path,argv[0],sizeof(path)-1);
#endif
sysblk.hercules_pgmname = strdup(basename(path));
#if !defined( _MSVC_ )
strncpy(path,argv[0],sizeof(path)-1);
#endif
sysblk.hercules_pgmpath = strdup(dirname(path));
}
}
else
{
sysblk.hercules_pgmname = strdup("hercules");
sysblk.hercules_pgmpath = strdup("");
}
#if defined(ENABLE_BUILTIN_SYMBOLS)
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", sizeof(num_procs) );
else
strlcpy( num_procs, "", sizeof(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 );
#endif
sysblk.sysgroup = DEFAULT_SYSGROUP;
sysblk.msglvl = DEFAULT_MLVL; /* Defaults to TERSE and DEVICES */
/* set default console port address */
sysblk.cnslport = strdup("3270");
/* set default tape autoinit value to OFF */
sysblk.noautoinit = TRUE;
/* default for system dasd cache is on */
sysblk.dasdcache = TRUE;
#if defined(OPTION_MSGCLR) || defined(OPTION_MSGHLD)
/* set default error message display (emsg) */
sysblk.emsg = EMSG_ON;
#endif
#if defined( OPTION_SHUTDOWN_CONFIRMATION )
/* set default quit timeout value (also ssd) */
sysblk.quitmout = QUITTIME_PERIOD;
#endif
/* Default command separator to off (NULL) */
sysblk.cmdsep = NULL;
#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 enabled */
sysblk.shcmdopt = SHCMDOPT_ENABLE + SHCMDOPT_DIAG8;
/* 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);
/* set default Program Interrupt Trace to NONE */
sysblk.pgminttr = OS_NONE;
sysblk.timerint = DEF_TOD_UPDATE_USECS;
/* set default thread priorities */
sysblk.hercprio = DEFAULT_HERCPRIO;
sysblk.todprio = DEFAULT_TOD_PRIO;
sysblk.cpuprio = DEFAULT_CPU_PRIO;
sysblk.devprio = DEFAULT_DEV_PRIO;
sysblk.srvprio = DEFAULT_SRV_PRIO;
/* Cap the default priorities at zero if setuid not available */
#if !defined( _MSVC_ )
#if !defined(NO_SETUID)
if (sysblk.suid)
#endif
{
if (sysblk.hercprio < 0)
sysblk.hercprio = 0;
if (sysblk.todprio < 0)
sysblk.todprio = 0;
if (sysblk.cpuprio < 0)
sysblk.cpuprio = 0;
if (sysblk.devprio < 0)
sysblk.devprio = 0;
if (sysblk.srvprio < 0)
sysblk.srvprio = 0;
}
#endif
#if defined(_FEATURE_ECPSVM)
sysblk.ecpsvm.available = 0;
sysblk.ecpsvm.level = 20;
#endif
#ifdef PANEL_REFRESH_RATE
sysblk.panrate = PANEL_REFRESH_RATE_SLOW;
#endif
#if defined( OPTION_SHUTDOWN_CONFIRMATION )
/* Set the quitmout value */
sysblk.quitmout = QUITTIME_PERIOD; /* quit timeout value */
#endif
#if defined(OPTION_SHARED_DEVICES)
sysblk.shrdport = 0;
#endif
#ifdef OPTION_MSGHLD
/* Set the default timeout value */
sysblk.keep_timeout_secs = 120;
#endif
#if defined(ENABLE_BUILTIN_SYMBOLS)
/* 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 );
}
#endif
#if defined(_FEATURE_CMPSC_ENHANCEMENT_FACILITY)
sysblk.zpbits = DEF_CMPSC_ZP_BITS;
#endif
/* Initialize locks, conditions, and attributes */
initialize_lock (&sysblk.config);
initialize_lock (&sysblk.todlock);
initialize_lock (&sysblk.mainlock);
sysblk.mainowner = LOCK_OWNER_NONE;
initialize_lock (&sysblk.intlock);
initialize_lock (&sysblk.iointqlk);
sysblk.intowner = LOCK_OWNER_NONE;
initialize_lock (&sysblk.sigplock);
initialize_lock (&sysblk.mntlock);
initialize_lock (&sysblk.scrlock);
initialize_lock (&sysblk.crwlock);
initialize_lock (&sysblk.ioqlock);
initialize_condition (&sysblk.ioqcond);
#if defined(OPTION_CMDSER)
initialize_lock (&sysblk.cmdlock);
initialize_condition (&sysblk.cmdcond);
#endif /*defined(OPTION_CMDSER)*/
#ifdef FEATURE_MESSAGE_SECURITY_ASSIST_EXTENSION_3
/* Initialize the wrapping key registers lock */
initialize_rwlock(&sysblk.wklock);
#endif
/* 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_condition (&sysblk.cpucond);
{
int i;
for (i = 0; i < MAX_CPU_ENGINES; i++)
initialize_lock (&sysblk.cpulock[i]);
}
initialize_condition (&sysblk.sync_cond);
initialize_condition (&sysblk.sync_bc_cond);
/* Copy length for regs */
sysblk.regs_copy_len = (int)((uintptr_t)&sysblk.dummyregs.regs_copy_end
- (uintptr_t)&sysblk.dummyregs);
/* Set the daemon_mode flag indicating whether we running in
background/daemon mode or not (meaning both stdout/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.
*/
sysblk.daemon_mode = !isatty(STDERR_FILENO) && !isatty(STDOUT_FILENO);
/* 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.
*/
logger_init();
/*
Setup the initial codepage
*/
set_codepage(NULL);
/* 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
daemon_mode (external gui mode) or not). This it the call that
the panel thread or the one the 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.
*/
display_version (stdout, "Hercules", TRUE);
#ifdef EXTERNALGUI
if (argc >= 1 && strncmp(argv[argc-1],"EXTERNALGUI",11) == 0)
{
e_gui = TRUE;
argc--;
}
#endif
#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) */
/* Get name of configuration file or default to hercules.cnf */
if(!(cfgfile = getenv("HERCULES_CNF")))
cfgfile = "hercules.cnf";
/* Process the command line options */
{
#define HERCULES_BASE_OPTS "hf:r:db:v"
#define HERCULES_SYM_OPTS ""
#define HERCULES_HDL_OPTS ""
#if defined(ENABLE_BUILTIN_SYMBOLS)
#undef HERCULES_SYM_OPTS
#define HERCULES_SYM_OPTS "s:"
#endif
#if defined(OPTION_DYNAMIC_LOAD)
#undef HERCULES_HDL_OPTS
#define HERCULES_HDL_OPTS "p:l:"
#endif
#define HERCULES_OPTS_STRING HERCULES_BASE_OPTS HERCULES_SYM_OPTS HERCULES_HDL_OPTS
#if defined(HAVE_GETOPT_LONG)
static struct option longopts[] =
{
{ "help", no_argument, NULL, 'h' },
{ "config", required_argument, NULL, 'f' },
{ "rcfile", required_argument, NULL, 'r' },
{ "daemon", no_argument, NULL, 'd' },
{ "herclogo", required_argument, NULL, 'b' },
{ "verbose", no_argument, NULL, 'v' },
#if defined(ENABLE_BUILTIN_SYMBOLS)
{ "defsym", required_argument, NULL, 's' },
#endif
#if defined(OPTION_DYNAMIC_LOAD)
{ "modpath", required_argument, NULL, 'p' },
{ "ldmod", required_argument, NULL, 'l' },
#endif
{ NULL, 0, NULL, 0 }
};
while ((c = getopt_long( argc, argv, HERCULES_OPTS_STRING, longopts, NULL )) != EOF)
#else
while ((c = getopt( argc, argv, HERCULES_OPTS_STRING )) != EOF)
#endif
{
switch (c) {
case 'h':
arg_error = 1;
break;
case 'f':
cfgfile = optarg;
break;
case 'r':
rcname = optarg;
break;
#if defined(ENABLE_BUILTIN_SYMBOLS)
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 != NULL && value != NULL )
{
int j;
for( j = 0; j < (int)strlen( sym ); j++ )
if ( islower( sym[j] ) )
{
sym[j] = toupper( sym[j] );
}
set_symbol(sym, value);
}
else
WRMSG(HHC01419, "E" );
}
else
WRMSG(HHC01419, "E");
}
break;
#endif
#if defined(OPTION_DYNAMIC_LOAD)
case 'p':
if(optarg)
hdl_setpath(strdup(optarg), FALSE);
break;
case 'l':
{
char *dllname, *strtok_str = NULL;
for(dllname = strtok_r(optarg,", ",&strtok_str);
dllname;
dllname = strtok_r(NULL,", ",&strtok_str))
{
if (dll_count < MAX_DLL_TO_LOAD - 1)
dll_load[++dll_count] = strdup(dllname);
else
{
WRMSG(HHC01406, "W", MAX_DLL_TO_LOAD);
break;
}
}
}
break;
#endif /* defined(OPTION_DYNAMIC_LOAD) */
case 'b':
sysblk.logofile = optarg;
break;
case 'v':
sysblk.msglvl |= MLVL_VERBOSE;
break;
case 'd':
sysblk.daemon_mode = 1;
break;
default:
arg_error = 1;
} /* end switch(c) */
} /* end while */
} /* end Process the command line options */
/* Treat filename None as special */
if(!strcasecmp(cfgfile,"None"))
cfgfile = NULL;
if (optind < argc)
arg_error = 1;
/* Terminate if invalid arguments were detected */
if (arg_error)
{
char pgm[MAX_PATH];
char* strtok_str = NULL;
strncpy(pgm, sysblk.hercules_pgmname, sizeof(pgm));
/* Show them all of our command-line arguments... */
WRMSG (HHC01414, "S", ""); // (blank line)
WRMSG (HHC01414, "S", ""); // (blank line)
#if defined(OPTION_DYNAMIC_LOAD)
// "Usage: %s [-f config-filename] [-d] [-b logo-filename] [-s sym=val]%s [> logfile]"
WRMSG (HHC01407, "S", strtok_r(pgm,".",&strtok_str),
" [-p dyn-load-dir] [[-l dynmod-to-load]...]");
#else
WRMSG (HHC01407, "S", strtok_r(pgm,".", &strtok_str), "");
#endif /* defined(OPTION_DYNAMIC_LOAD) */
WRMSG (HHC01414, "S", ""); // (blank line)
WRMSG (HHC01414, "S", ""); // (blank line)
fflush(stderr);
fflush(stdout);
usleep(100000);
return(1);
}
/* Set default TCP keepalive values */
#if !defined( HAVE_BASIC_KEEPALIVE ) && !defined( HAVE_FULL_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 // defined( HAVE_BASIC_KEEPALIVE ) || defined( HAVE_FULL_KEEPALIVE )
#if defined( HAVE_BASIC_KEEPALIVE ) && !defined( HAVE_FULL_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" );
#endif // defined( HAVE_BASIC_KEEPALIVE )
/*
** 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" );
/* 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_opcode_tables();
#if defined(OPTION_DYNAMIC_LOAD)
/* Initialize the hercules dynamic loader */
hdl_main();
/* Load modules requested at startup */
if (dll_count >= 0)
{
int hl_err = FALSE;
for ( dll_count = 0; dll_count < MAX_DLL_TO_LOAD; dll_count++ )
{
if (dll_load[dll_count] != NULL)
{
if (hdl_load(dll_load[dll_count], HDL_LOAD_DEFAULT) != 0)
{
hl_err = TRUE;
}
free(dll_load[dll_count]);
}
else
break;
}
if (hl_err)
{
usleep(10000); // give logger time to issue error message
WRMSG(HHC01408, "S");
delayed_exit(-1);
return(1);
}
}
#endif /* defined(OPTION_DYNAMIC_LOAD) */
#ifdef EXTERNALGUI
/* Set GUI flag if specified as final argument */
if (e_gui)
{
#if defined(OPTION_DYNAMIC_LOAD)
if (hdl_load("dyngui",HDL_LOAD_DEFAULT) != 0)
{
usleep(10000); /* (give logger thread time to issue
preceding HHC01516E message) */
WRMSG(HHC01409, "S");
delayed_exit(-1);
return(1);
}
#endif /* defined(OPTION_DYNAMIC_LOAD) */
}
#endif /*EXTERNALGUI*/
/* Register the SIGINT handler */
if ( signal (SIGINT, sigint_handler) == SIG_ERR )
{
WRMSG(HHC01410, "S", "SIGINT", strerror(errno));
delayed_exit(-1);
return(1);
}
/* Register the SIGTERM handler */
if ( signal (SIGTERM, sigterm_handler) == SIG_ERR )
{
WRMSG(HHC01410, "S", "SIGTERM", strerror(errno));
delayed_exit(-1);
return(1);
}
#if defined( _MSVC_ )
/* Register the Window console ctrl handlers */
if (!IsDebuggerPresent())
{
if (!SetConsoleCtrlHandler( console_ctrl_handler, TRUE ))
{
WRMSG( HHC01410, "S", "Console-ctrl", strerror( errno ));
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 )
{
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];
}
#if !defined(NO_SIGABEND_HANDLER)
{
struct sigaction sa;
sa.sa_sigaction = (void*)&sigabend_handler;
#ifdef SA_NODEFER
sa.sa_flags = SA_NODEFER;
#else
sa.sa_flags = 0;
#endif
if( sigaction(SIGILL, &sa, NULL)
|| sigaction(SIGFPE, &sa, NULL)
|| sigaction(SIGSEGV, &sa, NULL)
|| sigaction(SIGBUS, &sa, NULL)
|| sigaction(SIGUSR1, &sa, NULL)
|| sigaction(SIGUSR2, &sa, NULL) )
{
WRMSG(HHC01410, "S", "SIGILL/FPE/SEGV/BUS/USR", strerror(errno));
delayed_exit(-1);
return(1);
}
}
#endif /*!defined(NO_SIGABEND_HANDLER)*/
if(cfgfile)
{
/* attempt to get lock on config file */
hostpath(pathname, cfgfile, sizeof(pathname));
#if defined( OPTION_LOCK_CONFIG_FILE )
/* Test that we can get a read the file */
if ( ( fd_cfg = HOPEN( pathname, O_RDONLY, S_IRUSR | S_IRGRP ) ) < 0 )
{
if ( errno == EACCES )
{
WRMSG( HHC01453, "S", cfgfile, strerror( errno ) );
delayed_exit(-1);
return(1);
}
}
else
{
if ( lseek(fd_cfg, 0L, 2) < 0 )
{
if ( errno == EACCES )
{
WRMSG( HHC01453, "S", cfgfile, strerror( errno ) );
delayed_exit(-1);
return(1);
}
}
close( fd_cfg );
}
/* File was not lock, therefore we can proceed */
#endif // OPTION_LOCK_CONFIG_FILE
}
/* System initialisation time */
sysblk.todstart = hw_clock() << 8;
#if !defined(NO_SIGABEND_HANDLER)
/* Start the watchdog */
rc = create_thread (&sysblk.wdtid, DETACHED,
watchdog_thread, NULL, "watchdog_thread");
if (rc)
{
WRMSG(HHC00102, "E", strerror(rc));
delayed_exit(-1);
return(1);
}
#endif /*!defined(NO_SIGABEND_HANDLER)*/
if(log_callback)
{
// 'herclin' called us. IT'S 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)
WRMSG(HHC00102, "E", strerror(rc));
return(0);
}
hdl_adsc("release_config", release_config, NULL);
/* Build system configuration */
if ( build_config (cfgfile) )
{
delayed_exit(-1);
return(1);
}
/* Start up the RC file processing thread */
rc = create_thread(&rctid,DETACHED,
process_rc_file,NULL,"process_rc_file");
if (rc)
WRMSG(HHC00102, "E", strerror(rc));
#if defined( OPTION_LOCK_CONFIG_FILE )
if(cfgfile)
{
if ( ( fd_cfg = HOPEN( pathname, O_RDONLY, S_IRUSR | S_IRGRP ) ) < 0 )
{
WRMSG( HHC01432, "S", pathname, "open()", strerror( errno ) );
delayed_exit(-1);
return(1);
}
else
{
#if defined( _MSVC_ )
if( ( rc = _locking( fd_cfg, _LK_NBRLCK, 1L ) ) < 0 )
{
int rc = errno;
WRMSG( HHC01454, "S", pathname, "_locking()", strerror( errno ) );
delayed_exit(-1);
return(1);
}
#else
fl_cfg.l_type = F_RDLCK;
fl_cfg.l_whence = SEEK_SET;
fl_cfg.l_start = 0;
fl_cfg.l_len = 1;
if ( fcntl(fd_cfg, F_SETLK, &fl_cfg) == -1 )
{
if (errno == EACCES || errno == EAGAIN)
{
WRMSG( HHC01432, "S", pathname, "fcntl()", strerror( errno ) );
delayed_exit(-1);
return(1);
}
}
#endif
}
}
#endif // OPTION_LOCK_CONFIG_FILE
//---------------------------------------------------------------
// The below functions will not return until Hercules is shutdown
//---------------------------------------------------------------
/* Activate the control panel */
if(!sysblk.daemon_mode)
panel_display ();
else
{
#if defined(OPTION_DYNAMIC_LOAD)
if(daemon_task)
daemon_task ();
else
#endif /* defined(OPTION_DYNAMIC_LOAD) */
{
/* Tell RC file and HAO threads they may now proceed */
sysblk.panel_init = 1;
/* Retrieve messages from logger and write to stderr */
while (1)
if((msgcnt = log_read(&msgbuf, &msgnum, LOG_BLOCK)))
if(isatty(STDERR_FILENO))
fwrite(msgbuf,msgcnt,1,stderr);
}
}
// -----------------------------------------------------
// *** Hercules has been shutdown (PAST tense) ***
// -----------------------------------------------------
#if defined( OPTION_LOCK_CONFIG_FILE )
if(cfgfile)
close( fd_cfg ); // release config file lock
#endif // OPTION_LOCK_CONFIG_FILE
ASSERT( sysblk.shutdown ); // (why else would we be here?!)
#ifdef _MSVC_
SetConsoleCtrlHandler(console_ctrl_handler, FALSE);
socket_deinit();
#endif
#if defined(OPTION_MSGCLR) || defined(OPTION_MSGHLD)
if ( sysblk.emsg & EMSG_TEXT )
fprintf(stdout, HHC01412 );
else
#endif
fprintf(stdout, MSG(HHC01412, "I"));
fflush(stdout);
usleep(10000);
return 0;
} /* end function impl */
/*-------------------------------------------------------------------*/
/* System cleanup */
/*-------------------------------------------------------------------*/
DLL_EXPORT void system_cleanup (void)
{
/*
Currently only called by hdlmain,c's HDL_FINAL_SECTION
after the main 'hercules' module has been unloaded, but
that could change at some time in the future.
The above and below logmsg's are commented out since this
function currently doesn't do anything yet. Once it DOES
something, they should be uncommented.
*/
}