Files
org-hyperion-cules/panel.c
T
Jan Jaeger 38dc432335 Update copyright statements to include 2003
git-svn-id: file:///home/jj/hercules.svn/trunk@1107 956126f8-22a0-4046-8f4a-272fa8102e63
2003-01-05 12:17:12 +00:00

2375 lines
83 KiB
C

/* PANEL.C (c) Copyright Roger Bowler, 1999-2003 */
/* ESA/390 Control Panel Commands */
/* */
/* Modified for New Panel Display =NP= */
/*-------------------------------------------------------------------*/
/* This module is the control panel for the ESA/390 emulator. */
/* It provides functions for displaying the PSW and registers */
/* and a command line for requesting control operations such */
/* as IPL, stop, start, single stepping, instruction tracing, */
/* and storage displays. It displays messages issued by other */
/* threads via the logmsg macro, and optionally also writes */
/* all messages to a log file if stdout is redirected. */
/*-------------------------------------------------------------------*/
/*-------------------------------------------------------------------*/
/* Additional credits: */
/* breakpoint command contributed by Dan Horak */
/* devinit command contributed by Jay Maynard */
/* New Panel Display contributed by Dutch Owen */
/* HMC system console commands contributed by Jan Jaeger */
/* Set/reset bad frame indicator command by Jan Jaeger */
/* attach/detach/define commands by Jan Jaeger */
/* Panel refresh rate triva by Reed H. Petty */
/* z/Architecture support - (c) Copyright Jan Jaeger, 1999-2003 */
/* 64-bit address support by Roger Bowler */
/* Display subchannel command by Nobumichi Kozawa */
/* External GUI logic contributed by "Fish" (David B. Trout) */
/* Socket Devices originally designed by Malcolm Beattie; */
/* actual implementation by "Fish" (David B. Trout). */
/*-------------------------------------------------------------------*/
#include "hercules.h"
#include "devtype.h"
#include "opcode.h"
// #include "inline.h"
#if defined(OPTION_FISHIO)
#include "w32chan.h"
#endif /* defined(OPTION_FISHIO) */
#if defined(FISH_HANG)
extern int bFishHangAtExit; // (set to true when shutting down)
extern void FishHangInit(char* pszFileCreated, int nLineCreated);
extern void FishHangReport();
extern void FishHangAtExit();
#endif // defined(FISH_HANG)
#define DISPLAY_INSTRUCTION_OPERANDS
/* (Socket Devices...) */
int bind_device (DEVBLK* dev, char* spec);
int unbind_device (DEVBLK* dev);
int unix_socket (char* path);
int inet_socket (char* spec);
int add_socket_devices_to_fd_set (fd_set* mask, int maxfd);
void check_socket_devices_for_connections (fd_set* mask);
void socket_device_connection_handler (bind_struct* bs);
char* safe_strdup (char* str);
/*=NP================================================================*/
/* Global data for new panel display */
/* (Note: all NPD mods are identified by the string =NP= */
/*===================================================================*/
#define BLK 30
#define DGRY 1;30
#define BLU 34
#define LBLU 1;34
#define GRN 32
#define LGRN 1;32
#define CYN 36
#define LCYN 1;36
#define RED 31
#define LRED 1;31
#define PUR 35
#define LPUR 1;35
#define YLW 33
#define LYLW 1;33
#define LGRY 37
#define WHT 1;37
#define BBLK 40
#define BBLU 44
#define BGRN 42
#define BCYN 46
#define BRED 41
#define BPUR 45
#define BBRN 43
#define BLGRY 47
#define ANSI_CYN_BLK "\x1B[0;36;40m"
#define ANSI_WHT_BLU "\x1B[1;37;44m"
#define ANSI_WHT_BLK "\x1B[1;37;40m"
#define ANSI_GRN_BLK "\x1B[0;32;40m"
#define ANSI_RED_BLK "\x1B[1;31;40m"
#define ANSI_YLW_BLK "\x1B[1;33;40m"
#define ANSI_GRY_BLU "\x1B[1;30;44m"
#define ANSI_WHT_BLU "\x1B[1;37;44m"
#define ANSI_WHT_GRN "\x1B[1;37;42m"
#define ANSI_GRY_GRN "\x1B[1;30;42m"
#define ANSI_WHT_RED "\x1B[1;37;41m"
#define ANSI_GRY_RED "\x1B[1;30;41m"
#define ANSI_GRY_BLK "\x1B[0m"
#define ANSI_LGRN_BLK "\x1B[1;32;40m"
#define ANSI_CLEAR "\x1B[2J"
#define ANSI_CLEAR_EOL "\x1B[K"
#define ANSI_CURSOR "\x1B[%d;%dH"
int NPDup = 0; /* 1 when new panel is up */
int NPDinit = 0; /* 1 when new panel is initialized */
int NPhelpup = 0; /* 1 when displaying help panel */
int NPhelpdown = 0; /* 1 when the help panel is brought down */
int NPregdisp = 0; /* which regs are displayed 0=gpr, 1=cr, 2=ar, 3=fpr */
int NPaddress = 0; /* Address switches */
int NPdata = 0; /* Data switches */
int NPipl = 0; /* IPL address switches */
int NPcmd = 0; /* 1 when command mode for NP is in effect */
int NPdataentry = 0; /* 1 when data entry for NP is in progress */
int NPdevsel = 0; /* 1 when device selection is in progress */
char NPpending; /* Command which is pending data entry */
char NPentered[128]; /* Data which was entered */
char NPprompt1[40]; /* Prompts for left and right bottom of screen */
char NPprompt2[40];
char NPsel2; /* Command letter to trigger 2nd phase of dev sel */
char NPdevice; /* Which device selected */
int NPasgn; /* Index to device being reassigned */
int NPlastdev; /* Number of devices */
int NPdevaddr[24]; /* current device addresses */
char NPdevstr[16]; /* device - stringed */
/* the following fields are current states, to detect changes and redisplay */
char NPstate[24]; /* Current displayed CPU state */
U32 NPregs[16]; /* Current displayed reg values */
int NPbusy[24]; /* Current busy state of displayed devices */
int NPpend[24]; /* Current int pending state */
int NPopen[24]; /* Current open state */
int NPonline[24]; /* Current online state of devices */
char NPdevname[24][128]; /* Current name assignments */
int NPcuraddr; /* current addr switches */
int NPcurdata; /* current data switches */
int NPcurrg; /* current register set displayed */
int NPcuripl; /* current IPL switches */
int NPcurpos[2]; /* Cursor position (row, col) */
char NPcolor[24]; /* color string */
int NPdatalen; /* Length of data */
char NPcurprompt1[40];
char NPcurprompt2[40];
U32 NPaaddr;
#ifdef EXTERNALGUI
/*-------------------------------------------------------------------*/
/* External GUI control */
/*-------------------------------------------------------------------*/
#ifdef OPTION_MIPS_COUNTING
U32 mipsrate = 0;
U32 siosrate = 0;
U32 prevmipsrate = 0;
U32 prevsiosrate = 0;
int gui_cpupct = 0; /* 1=cpu percentage active */
#endif /*OPTION_MIPS_COUNTING*/
int gui_gregs = 1; /* 1=gregs status active */
int gui_cregs = 1; /* 1=cregs status active */
int gui_aregs = 1; /* 1=aregs status active */
int gui_fregs = 1; /* 1=fregs status active */
int gui_devlist = 1; /* 1=devlist status active */
DEVBLK* dev;
BYTE* devclass;
char devnam[256];
int stat_online, stat_busy, stat_pend, stat_open;
#endif /*EXTERNALGUI*/
/*=NP================================================================*/
/* Initialize the NP data */
/*===================================================================*/
static void NP_init()
{
int i;
for (i = 0; i < 16; i++) {
NPregs[i] = -1;
}
for (i = 0; i < 24; i++) {
NPbusy[i] = NPpend[i] = NPopen[i] = 0;
NPonline[i] = 0;
strcpy(NPdevname[i], "");
}
strcpy(NPstate, "U");
NPcuraddr = NPcurdata = NPcurrg = -1;
NPcuripl = -1;
NPcurpos[0] = 1;
NPcurpos[1] = 1;
strcpy(NPcolor, "");
strcpy(NPprompt1, "");
strcpy(NPprompt2, "");
strcpy(NPcurprompt1, "");
strcpy(NPcurprompt2, "");
}
/*=NP================================================================*/
/* This draws the initial screen template */
/*===================================================================*/
static void NP_screen(FILE *confp)
{
DEVBLK *dev;
BYTE *devclass;
int p, a;
char c[2];
char devnam[128];
fprintf(confp, ANSI_WHT_BLK);
fprintf(confp, ANSI_CLEAR);
fprintf(confp, ANSI_WHT_BLU);
fprintf(confp, ANSI_CURSOR, 1, 1);
fprintf(confp, " Hercules CPU %7.7s ",
get_arch_mode_string(NULL));
fprintf(confp, ANSI_CURSOR, 1, 38);
fprintf(confp, "| Peripherals ");
fprintf(confp, ANSI_GRY_BLK);
fprintf(confp, ANSI_CURSOR, 2, 39);
fprintf(confp, " # Addr Modl Type Assignment ");
fprintf(confp, ANSI_CURSOR, 4, 9);
fprintf(confp, "PSW");
fprintf(confp, ANSI_CURSOR, 7, 9);
fprintf(confp, "0 1 2 3");
fprintf(confp, ANSI_CURSOR, 9, 9);
fprintf(confp, "4 5 6 7");
fprintf(confp, ANSI_CURSOR, 11, 9);
fprintf(confp, "8 9 10 11");
fprintf(confp, ANSI_CURSOR, 13, 8);
fprintf(confp, "12 13 14 15");
fprintf(confp, ANSI_CURSOR, 14, 6);
fprintf(confp, "GPR CR AR FPR");
fprintf(confp, ANSI_CURSOR, 16, 2);
fprintf(confp, "ADDRESS:");
fprintf(confp, ANSI_CURSOR, 16, 22);
fprintf(confp, "DATA:");
fprintf(confp, ANSI_CURSOR, 20, 2);
#ifdef OPTION_MIPS_COUNTING
fprintf(confp, " MIPS SIO/s");
#else
fprintf(confp, "instructions");
#endif
p = 3;
a = 1;
for (dev = sysblk.firstdev; dev != NULL; dev = dev->nextdev)
{
if(dev->pmcw.flag5 & PMCW5_V)
{
fprintf(confp, ANSI_CURSOR, p, 40);
c[0] = a | 0x40;
c[1] = '\0';
(dev->hnd->query)(dev, &devclass, sizeof(devnam), devnam);
fprintf(confp, "%s %4.4X %4.4X %-4.4s %.24s",
c, dev->devnum, dev->devtype, devclass, devnam);
strcpy(NPdevname[a - 1], devnam);
NPbusy[a - 1] = 0;
NPbusy[a - 1] = 0;
NPdevaddr[a - 1] = dev->devnum;
p++;
a++;
if (p > 23) break;
}
}
NPlastdev = a;
fprintf(confp, ANSI_WHT_BLK);
for (p = 2; p < 25; p++) {
fprintf(confp, ANSI_CURSOR, p, 38);
fprintf(confp, "|");
}
fprintf(confp, ANSI_CURSOR, 18, 1);
fprintf(confp, "-------------------------------------");
fprintf(confp, ANSI_CURSOR, 24, 1);
fprintf(confp, "-------------------------------------");
fprintf(confp, ANSI_CURSOR, 24, 39);
fprintf(confp, "------------------------------------------");
fprintf(confp, ANSI_GRY_BLU);
fprintf(confp, ANSI_CURSOR " STO ", 19, 16);
fprintf(confp, ANSI_GRY_BLU);
fprintf(confp, ANSI_CURSOR " DIS ", 19, 24);
fprintf(confp, ANSI_GRY_BLU);
fprintf(confp, ANSI_CURSOR " EXT ", 22, 16);
fprintf(confp, ANSI_GRY_BLU);
fprintf(confp, ANSI_CURSOR " IPL ", 22, 24);
fprintf(confp, ANSI_GRY_GRN);
fprintf(confp, ANSI_CURSOR " STR ", 22, 2);
fprintf(confp, ANSI_GRY_RED);
fprintf(confp, ANSI_CURSOR " STP ", 22, 9);
fprintf(confp, ANSI_GRY_BLU);
fprintf(confp, ANSI_CURSOR " RST ", 19, 32);
fprintf(confp, ANSI_GRY_RED);
fprintf(confp, ANSI_CURSOR " PWR ", 22, 32);
fprintf(confp, ANSI_WHT_BLK);
fprintf(confp, ANSI_CURSOR "G", 14, 6);
fprintf(confp, ANSI_CURSOR "C", 14, 14);
fprintf(confp, ANSI_CURSOR "A", 14, 22);
fprintf(confp, ANSI_CURSOR "F", 14, 30);
fprintf(confp, ANSI_CURSOR "U", 2, 40);
fprintf(confp, ANSI_CURSOR "n", 2, 62);
fprintf(confp, ANSI_CURSOR "R", 16, 5);
fprintf(confp, ANSI_CURSOR "D", 16, 22);
fprintf(confp, ANSI_WHT_BLU);
fprintf(confp, ANSI_CURSOR "O", 19, 19);
fprintf(confp, ANSI_CURSOR "I", 19, 26);
fprintf(confp, ANSI_CURSOR "E", 22, 17);
fprintf(confp, ANSI_CURSOR "L", 22, 27);
fprintf(confp, ANSI_CURSOR "T", 19, 35);
fprintf(confp, ANSI_WHT_GRN);
fprintf(confp, ANSI_CURSOR "S", 22, 3);
fprintf(confp, ANSI_WHT_RED);
fprintf(confp, ANSI_CURSOR "P", 22, 12);
fprintf(confp, ANSI_CURSOR "W", 22, 34);
}
/*=NP================================================================*/
/* This refreshes the screen with new data every cycle */
/*===================================================================*/
static void NP_update(FILE *confp, char *cmdline, int cmdoff)
{
int s, i, r, c;
int online, busy, pend, open;
QWORD curpsw;
U32 curreg[16];
char state[24];
char dclear[128];
char devnam[128];
REGS *regs;
BYTE pswwait; /* PSW wait state bit */
DEVBLK *dev;
BYTE *devclass;
int p, a;
char ch[2];
U32 aaddr;
int savadr;
#ifdef OPTION_MIPS_COUNTING
U32 mipsrate;
U32 siosrate;
#endif
if (NPhelpup == 1) {
if (NPhelpdown == 1) {
NP_init();
NP_screen(confp);
NPhelpup = 0;
NPhelpdown = 0;
} else {
fprintf(confp, ANSI_GRY_BLK);
fprintf(confp, ANSI_CLEAR);
fprintf(confp, ANSI_CURSOR, 1, 1);
fprintf(confp, "All commands consist of one character keypresses. The various commands are\n");
fprintf(confp, "highlighted onscreen by bright white versus the gray of other lettering. \n");
fprintf(confp, "\n");
fprintf(confp, "Press the escape key to terminate the control panel and go to command mode.\n");
fprintf(confp, "\n");
fprintf(confp, "Display Controls: G - General purpose regs C - Control regs\n");
fprintf(confp, " A - Access registers F - Floating Point regs\n");
fprintf(confp, " I - Display main memory at 'ADDRESS'\n");
fprintf(confp, "CPU controls: L - IPL S - Start CPU\n");
fprintf(confp, " E - External interrupt P - Stop CPU\n");
fprintf(confp, " W - Exit Hercules T - Restart interrupt\n");
fprintf(confp, "Data Manipulation: R - enter setting for the 'ADDRESS switches'\n");
fprintf(confp, " D - enter data for 'data' switches\n");
fprintf(confp, " O - place value in 'DATA' in memory at 'ADDRESS'.\n");
fprintf(confp, "\n");
fprintf(confp, "Peripherals: N - enter a new name for the device file assignment\n");
fprintf(confp, " U - send an I/O attention interrupt\n");
fprintf(confp, "\n");
fprintf(confp, "In the display of devices, a green device letter means the device is online,\n");
fprintf(confp, "a lighted device address means the device is busy, and a green model number\n");
fprintf(confp, "means the attached UNIX file is open to the device.\n");
fprintf(confp, "\n");
fprintf(confp, ANSI_CURSOR, 24, 16);
fprintf(confp, "Press Escape to return to control panel operations");
return;
}
}
regs = sysblk.regs + sysblk.pcpu;
#if defined(OPTION_MIPS_COUNTING)
fprintf(confp, ANSI_WHT_BLU);
fprintf(confp, ANSI_CURSOR, 1, 16);
fprintf(confp, "%4.4X:",regs->cpuad);
fprintf(confp, ANSI_CURSOR, 1, 22);
fprintf(confp, "%3d%%",(int)(100.0 * regs->cpupct));
#endif /*defined(OPTION_MIPS_COUNTING)*/
#if defined(_FEATURE_SIE)
if(regs->sie_active)
regs = regs->guestregs;
#endif /*defined(_FEATURE_SIE)*/
memset (curpsw, 0x00, sizeof(curpsw));
store_psw (regs, curpsw);
pswwait = curpsw[1] & 0x02;
fprintf (confp, ANSI_YLW_BLK);
fprintf (confp, ANSI_CURSOR, 3, 2);
fprintf (confp, "%2.2X%2.2X%2.2X%2.2X %2.2X%2.2X%2.2X%2.2X",
curpsw[0], curpsw[1], curpsw[2], curpsw[3],
curpsw[4], curpsw[5], curpsw[6], curpsw[7]);
sprintf (state, "%c%c%c%c%c%c%c%c",
regs->cpustate == CPUSTATE_STOPPED ? 'M' : '.',
sysblk.inststep ? 'T' : '.',
pswwait ? 'W' : '.',
regs->loadstate ? 'L' : '.',
regs->checkstop ? 'C' : '.',
regs->psw.prob ? 'P' : '.',
#if defined(_FEATURE_SIE)
regs->sie_state ? 'S' : '.',
#else /*!defined(_FEATURE_SIE)*/
'.',
#endif /*!defined(_FEATURE_SIE)*/
#if defined(_900)
regs->arch_mode == ARCH_900 ? 'Z' : '.');
#else
' ');
#endif
s = 20 + ((17 - strlen(state)) / 2);
if (strcmp(state, NPstate) != 0) {
fprintf (confp, ANSI_CURSOR, 3, 20);
fprintf (confp, " ");
fprintf (confp, ANSI_CURSOR, 3, s);
fprintf (confp, "%s", state);
strcpy(NPstate, state);
}
savadr = NPaddress;
for (i = 0; i < 16; i++) {
switch (NPregdisp) {
case 0:
curreg[i] = regs->GR_L(i);
break;
case 1:
curreg[i] = regs->CR_L(i);
break;
case 2:
curreg[i] = regs->AR(i);
break;
case 3:
if (i < 8) {
curreg[i] = regs->fpr[i];
} else {
curreg[i] = 0;
}
break;
case 4:
aaddr = APPLY_PREFIXING (NPaddress, regs->PX);
if (aaddr > regs->mainlim)
break;
curreg[i] = 0;
curreg[i] |= ((regs->mainstor[aaddr++] << 24) & 0xFF000000);
curreg[i] |= ((regs->mainstor[aaddr++] << 16) & 0x00FF0000);
curreg[i] |= ((regs->mainstor[aaddr++] << 8) & 0x0000FF00);
curreg[i] |= ((regs->mainstor[aaddr++]) & 0x000000FF);
NPaddress += 4;
break;
default:
curreg[i] = 0;
break;
}
}
NPaddress = savadr;
r = 6;
c = 2;
for (i = 0; i < 16; i++) {
if (curreg[i] != NPregs[i]) {
fprintf(confp, ANSI_CURSOR, r, c);
fprintf(confp, "%8.8X", curreg[i]);
NPregs[i] = curreg[i];
}
c += 9;
if (c > 36) {
c = 2;
r += 2;
}
}
fprintf(confp, ANSI_CURSOR, 19, 2);
fprintf(confp, ANSI_YLW_BLK);
#ifdef OPTION_MIPS_COUNTING
#ifdef _FEATURE_CPU_RECONFIG
for(mipsrate = siosrate = i = 0; i < MAX_CPU_ENGINES; i++)
if(sysblk.regs[i].cpuonline)
#else /*!_FEATURE_CPU_RECONFIG*/
for(mipsrate = siosrate = i = 0; i < sysblk.numcpu; i++)
#endif /*!_FEATURE_CPU_RECONFIG*/
{
mipsrate += sysblk.regs[i].mipsrate;
siosrate += sysblk.regs[i].siosrate;
}
if (mipsrate > 100000) mipsrate = 0; /* ignore wildly high rate */
fprintf(confp, "%2.1d.%2.2d %5d",
mipsrate / 1000, (mipsrate % 1000) / 10,
siosrate);
#else
fprintf(confp, "%12.12u",
#if defined(_FEATURE_SIE)
regs->sie_state ? (unsigned)regs->hostregs->instcount :
#endif /*defined(_FEATURE_SIE)*/
(unsigned)regs->instcount);
#endif
if (NPaddress != NPcuraddr) {
fprintf(confp, ANSI_YLW_BLK);
fprintf(confp, ANSI_CURSOR, 16, 11);
fprintf(confp, "%8.8X", NPaddress);
NPcuraddr = NPaddress;
}
if (NPdata != NPcurdata) {
fprintf(confp, ANSI_YLW_BLK);
fprintf(confp, ANSI_CURSOR, 16, 29);
fprintf(confp, "%8.8X", NPdata);
NPcurdata = NPdata;
}
if (NPregdisp != NPcurrg) {
fprintf(confp, ANSI_WHT_BLK);
switch (NPcurrg) {
case 0:
fprintf(confp, ANSI_CURSOR "G" , 14, 6);
break;
case 1:
fprintf(confp, ANSI_CURSOR "C" , 14, 14);
break;
case 2:
fprintf(confp, ANSI_CURSOR "A" , 14, 22);
break;
case 3:
fprintf(confp, ANSI_CURSOR "F" , 14, 30);
break;
default:
break;
}
NPcurrg = NPregdisp;
fprintf(confp, ANSI_YLW_BLK);
switch (NPregdisp) {
case 0:
fprintf(confp, ANSI_CURSOR "G" , 14, 6);
break;
case 1:
fprintf(confp, ANSI_CURSOR "C" , 14, 14);
break;
case 2:
fprintf(confp, ANSI_CURSOR "A" , 14, 22);
break;
case 3:
fprintf(confp, ANSI_CURSOR "F" , 14, 30);
break;
default:
break;
}
}
p = 3;
a = 1;
for (dev = sysblk.firstdev; dev != NULL; dev = dev->nextdev)
if(dev->pmcw.flag5 & PMCW5_V)
{
online = busy = pend = open = 0;
if ((dev->console && dev->connected) ||
(strlen(dev->filename) > 0))
online = 1;
if (dev->busy) busy = 1;
if (dev->pending) pend = 1;
if (dev->fd > 2) open = 1;
if (online != NPonline[a - 1]) {
fprintf(confp, ANSI_CURSOR, p, 40);
ch[0] = a | 0x40;
ch[1] = '\0';
if (online) {
fprintf(confp, ANSI_LGRN_BLK);
} else {
fprintf(confp, ANSI_GRY_BLK);
}
fprintf(confp, "%s", ch);
NPonline[a - 1] = online;
}
if (busy != NPbusy[a - 1] || pend != NPpend[a - 1]) {
fprintf(confp, ANSI_CURSOR, p, 42);
if (busy | pend) {
fprintf(confp, ANSI_YLW_BLK);
} else {
fprintf(confp, ANSI_GRY_BLK);
}
fprintf(confp, "%4.4X", dev->devnum);
NPbusy[a - 1] = busy;
NPpend[a - 1] = pend;
}
if (open != NPopen[a - 1]) {
fprintf(confp, ANSI_CURSOR, p, 47);
if (open) {
fprintf(confp, ANSI_LGRN_BLK);
} else {
fprintf(confp, ANSI_GRY_BLK);
}
fprintf(confp, "%4.4X", dev->devtype);
NPopen[a - 1] = open;
}
(dev->hnd->query)(dev, &devclass, sizeof(devnam), devnam);
if (strcmp(NPdevname[a - 1], devnam) != 0) {
fprintf(confp, ANSI_GRY_BLK);
fprintf(confp, ANSI_CURSOR, p, 57);
fprintf(confp, "%.24s" ANSI_CLEAR_EOL, devnam);
strcpy(NPdevname[a - 1], devnam);
}
p++;
a++;
if (p > 23) break;
}
if (strcmp(NPprompt1, NPcurprompt1) != 0) {
strcpy(NPcurprompt1, NPprompt1);
if (strlen(NPprompt1) > 0) {
s = 2 + ((38 - strlen(NPprompt1)) / 2);
fprintf(confp, ANSI_CURSOR, 24, s);
fprintf(confp, ANSI_WHT_BLU);
fprintf(confp, NPprompt1);
} else {
fprintf(confp, ANSI_WHT_BLK);
fprintf(confp, ANSI_CURSOR, 24, 1);
fprintf(confp, "-------------------------------------");
}
}
if (strcmp(NPprompt2, NPcurprompt2) != 0) {
strcpy(NPcurprompt2, NPprompt2);
if (strlen(NPprompt2) > 0) {
s = 42 + ((38 - strlen(NPprompt2)) / 2);
fprintf(confp, ANSI_CURSOR, 24, s);
fprintf(confp, ANSI_WHT_BLU);
fprintf(confp, NPprompt2);
} else {
fprintf(confp, ANSI_WHT_BLK);
fprintf(confp, ANSI_CURSOR, 24, 39);
fprintf(confp, "------------------------------------------");
}
}
if (NPdataentry) {
fprintf(confp, ANSI_CURSOR, NPcurpos[0], NPcurpos[1]);
if (strlen(NPcolor) > 0) {
fprintf(confp, NPcolor);
}
strcpy(dclear, "");
for (i = 0; i < NPdatalen; i++) dclear[i] = ' ';
dclear[i] = '\0';
fprintf(confp, dclear);
fprintf(confp, ANSI_CURSOR, NPcurpos[0], NPcurpos[1]);
for (i = 0; i < cmdoff; i++) putc (cmdline[i], confp);
} else {
fprintf(confp, ANSI_CURSOR, 24, 80);
NPcurpos[0] = 24;
NPcurpos[1] = 80;
}
}
/* ============== End of the main NP block of code =============*/
/*-------------------------------------------------------------------*/
/* Definitions for ANSI control sequences */
/*-------------------------------------------------------------------*/
#define ANSI_SAVE_CURSOR "\x1B[s"
#define ANSI_CURSOR_UP "\x1B[1A"
#define ANSI_CURSOR_DOWN "\x1B[1B"
#define ANSI_CURSOR_FORWARD "\x1B[1C"
#define ANSI_CURSOR_BACKWARD "\x1B[1D"
#define ANSI_POSITION_CURSOR "\x1B[%d;%dH"
#define ANSI_ROW1_COL1 "\x1B[1;1H"
#define ANSI_ROW1_COL80 "\x1B[1;80H"
#define ANSI_ROW22_COL80 "\x1B[22;80H"
#define ANSI_ROW23_COL1 "\x1B[23;1H"
#define ANSI_ROW24_COL1 "\x1B[24;1H"
#define ANSI_ROW24_COL79 "\x1B[24;79H"
#define ANSI_BLACK_GREEN "\x1B[30;42m"
#define ANSI_YELLOW_RED "\x1B[33;1;41m"
#define ANSI_WHITE_BLACK "\x1B[0m"
#define ANSI_HIGH_INTENSITY "\x1B[1m"
#define ANSI_ERASE_EOL "\x1B[K"
#define ANSI_ERASE_SCREEN "\x1B[2J"
#define ANSI_RESTORE_CURSOR "\x1B[u"
/*-------------------------------------------------------------------*/
/* Definitions for keyboard input sequences */
/*-------------------------------------------------------------------*/
#define KBD_HOME "\x1B[1~"
#define KBD_INSERT "\x1B[2~"
#define KBD_DELETE "\x1B[3~"
#define KBD_END "\x1B[4~"
#define KBD_PAGE_UP "\x1B[5~"
#define KBD_PAGE_DOWN "\x1B[6~"
#define KBD_UP_ARROW "\x1B[A"
#define KBD_DOWN_ARROW "\x1B[B"
#define KBD_RIGHT_ARROW "\x1B[C"
#define KBD_LEFT_ARROW "\x1B[D"
/*-------------------------------------------------------------------*/
/* Cleanup routine */
/*-------------------------------------------------------------------*/
static void system_cleanup (void)
{
struct termios kbattr; /* Terminal I/O structure */
/* Restore the terminal mode */
tcgetattr (STDIN_FILENO, &kbattr);
kbattr.c_lflag |= (ECHO | ICANON);
tcsetattr (STDIN_FILENO, TCSANOW, &kbattr);
#ifdef EXTERNALGUI
if (!extgui)
#endif /*EXTERNALGUI*/
/* Reset the cursor position */
fprintf (stderr,
ANSI_ROW24_COL79
ANSI_WHITE_BLACK
"\n");
fprintf (stderr,
"HHCIN007I Hercules terminated\n");
} /* end function system_cleanup */
/*-------------------------------------------------------------------*/
/* Process .RC file thread */
/*-------------------------------------------------------------------*/
int rc_thread_done = 0; /* 1 = RC file processed */
void* process_rc_file (void* dummy)
{
BYTE *rcname; /* hercules.rc name pointer */
FILE *rcfp; /* RC file pointer */
size_t rcbufsize = 1024; /* Size of RC file buffer */
BYTE *rcbuf = NULL; /* RC file input buffer */
int rclen; /* length of RC file record */
int rc_pause_amt = 0; /* seconds to pause RC file */
BYTE *p; /* (work) */
UNREFERENCED(dummy);
/* Obtain the name of the hercules.rc file or default */
if(!(rcname = getenv("HERCULES_RC")))
rcname = "hercules.rc";
/* Open RC file. If it doesn't exist,
then there's nothing for us to do */
if (!(rcfp = fopen(rcname, "r")))
{
if (ENOENT != errno)
logmsg(_("HHCPN007E RC file %s open failed: %s\n"),
rcname, strerror(errno));
rc_thread_done = 1;
return NULL;
}
logmsg(_("HHCPN008I RC file processing thread started using file %s\n"),
rcname);
/* Obtain storage for the RC file buffer */
if (!(rcbuf = malloc (rcbufsize)))
{
logmsg(_("HHCPN009E RC file buffer malloc failed: %s\n"),
strerror(errno));
fclose(rcfp);
rc_thread_done = 1;
return NULL;
}
for (;;)
{
/* Read a complete line from the RC file */
if (!fgets(rcbuf, rcbufsize, rcfp)) break;
/* Remove trailing whitespace */
for (rclen = strlen(rcbuf); rclen && isspace(rcbuf[rclen-1]); rclen--);
rcbuf[rclen] = 0;
/* '#' == silent comment, '*' == loud comment */
if ('#' == rcbuf[0] || '*' == rcbuf[0])
{
if ('*' == rcbuf[0])
logmsg ("> %s",rcbuf);
continue;
}
/* Remove any # comments on the line before processing */
if ((p = strchr(rcbuf,'#')) && p > rcbuf)
do *p = 0; while (isspace(*--p) && p >= rcbuf);
if (strncasecmp(rcbuf,"pause",5) == 0)
{
sscanf(rcbuf+5, "%d", &rc_pause_amt);
if (rc_pause_amt < 0 || rc_pause_amt > 999)
{
logmsg(_("HHCPN010W Ignoring invalid RC file pause "
"statement: %s\n"),
rcbuf+5);
continue;
}
logmsg (_("HHCPN011I Pausing RC file processing for %d "
"seconds...\n"),
rc_pause_amt);
sleep(rc_pause_amt);
logmsg (_("HHCPN012I Resuming RC file processing...\n"));
continue;
}
/* Process the command */
for (p = rcbuf; isspace(*p); p++);
SYNCHRONOUS_PANEL_CMD(p);
}
if (feof(rcfp))
logmsg (_("HHCPN013I EOF reached on RC file. Processing complete.\n"));
else
logmsg (_("HHCPN014E I/O error reading RC file: %s\n"),
strerror(errno));
fclose(rcfp);
rc_thread_done = 1;
return NULL;
}
/*-------------------------------------------------------------------*/
/* Panel display thread */
/* */
/* This function runs on the main thread. It receives messages */
/* from other threads and displays them on the screen. It accepts */
/* panel commands from the keyboard and executes them. It samples */
/* the PSW periodically and displays it on the screen status line. */
/* */
/* Note that this routine must not attempt to write messages into */
/* the message pipe by calling the logmsg function, because this */
/* will cause a deadlock when the pipe becomes full during periods */
/* of high message activity. For this reason a separate thread is */
/* created to process all commands entered. */
/*-------------------------------------------------------------------*/
int volatile initdone = 0; /* Initialization complete flag */
#define MAX_MSGS 800 /* Number of slots in buffer */
#define MSG_SIZE 80 /* Size of one message */
#define BUF_SIZE (MAX_MSGS*MSG_SIZE) /* Total size of buffer */
#define NUM_LINES 22 /* Number of scrolling lines */
#define CMD_SIZE 32767 /* Length of command line */
BYTE *msgbuf; /* Circular message buffer */
int msgslot = 0; /* Next available buffer slot*/
int nummsgs = 0; /* Number of msgs in buffer */
int msg_size = MSG_SIZE;
int max_msgs = MAX_MSGS;
void get_msgbuf(BYTE **_msgbuf, int *_msgslot, int *_nummsgs, int *_msg_size, int *_max_msgs)
{
*_msgbuf = msgbuf;
*_msgslot = msgslot;
*_nummsgs = nummsgs;
*_msg_size = msg_size;
*_max_msgs = max_msgs;
}
/* (forward references) */
#ifdef EXTERNALGUI
void gui_devlist_status (FILE *confp);
#endif
void panel_display (void)
{
int rc; /* Return code */
int i, n; /* Array subscripts */
REGS *regs; /* -> CPU register context */
QWORD curpsw; /* Current PSW */
QWORD prvpsw; /* Previous PSW */
BYTE prvstate = 0xFF; /* Previous stopped state */
U64 prvicount = 0; /* Previous instruction count*/
BYTE pswwait; /* PSW wait state bit */
int firstmsgn = 0; /* Number of first message to
be displayed relative to
oldest message in buffer */
#ifdef EXTERNALGUI
BYTE redraw_msgs = 0; /* 1=Redraw message area */
BYTE redraw_cmd = 0; /* 1=Redraw command line */
BYTE redraw_status = 0; /* 1=Redraw status line */
#else /*!EXTERNALGUI*/
BYTE redraw_msgs; /* 1=Redraw message area */
BYTE redraw_cmd; /* 1=Redraw command line */
BYTE redraw_status; /* 1=Redraw status line */
#endif /*EXTERNALGUI*/
BYTE readbuf[MSG_SIZE]; /* Message read buffer */
int readoff = 0; /* Number of bytes in readbuf*/
BYTE cmdline[CMD_SIZE+1]; /* Command line buffer */
int cmdoff = 0; /* Number of bytes in cmdline*/
TID cmdtid; /* Command thread identifier */
TID rctid; /* RC file thread identifier */
BYTE c; /* Character work area */
FILE *confp; /* Console file pointer */
FILE *logfp; /* Log file pointer */
struct termios kbattr; /* Terminal I/O structure */
size_t kbbufsize = CMD_SIZE; /* Size of keyboard buffer */
BYTE *kbbuf = NULL; /* Keyboard input buffer */
int kblen; /* Number of chars in kbbuf */
int pipefd; /* Pipe file descriptor */
int keybfd; /* Keyboard file descriptor */
int maxfd; /* Highest file descriptor */
fd_set readset; /* Select file descriptors */
struct timeval tv; /* Select timeout structure */
/* Display thread started message on control panel */
logmsg (_("HHCPN001I Control panel thread started: "
"tid="TIDPAT", pid=%d\n"),
thread_id(), getpid());
/* Obtain storage for the keyboard buffer */
if (!(kbbuf = malloc (kbbufsize)))
{
logmsg(_("HHCPN002S Cannot obtain keyboard buffer: %s\n"),
strerror(errno));
return;
}
/* Obtain storage for the circular message buffer */
msgbuf = malloc (BUF_SIZE);
if (msgbuf == NULL)
{
fprintf (stderr,
_("HHCPN003S Cannot obtain message buffer: %s\n"),
strerror(errno));
return;
}
/* If stdout is not redirected, then write screen output
to stdout and do not produce a log file. If stdout is
redirected, then write screen output to stderr and
write the logfile to stdout */
if (isatty(STDOUT_FILENO))
{
confp = stdout;
logfp = NULL;
}
else
{
confp = stderr;
logfp = stdout;
/* Logfile should be unbuffered to be always in sync */
setvbuf(logfp, NULL, _IONBF, 0);
}
/* Set screen output stream to fully buffered */
setvbuf (confp, NULL, _IOFBF, 0);
/* Set up the input file descriptors */
pipefd = sysblk.msgpiper;
keybfd = STDIN_FILENO;
/* Register the system cleanup exit routine */
atexit (system_cleanup);
/* Put the terminal into cbreak mode */
tcgetattr (keybfd, &kbattr);
kbattr.c_lflag &= ~(ECHO | ICANON);
kbattr.c_cc[VMIN] = 0;
kbattr.c_cc[VTIME] = 0;
tcsetattr (keybfd, TCSANOW, &kbattr);
#ifdef EXTERNALGUI
if (!extgui)
#endif /*EXTERNALGUI*/
/* Clear the screen */
fprintf (confp,
ANSI_WHT_BLK
ANSI_ERASE_SCREEN);
#ifdef EXTERNALGUI
if (!extgui)
{
#endif /*EXTERNALGUI*/
redraw_msgs = 1;
redraw_cmd = 1;
#ifdef EXTERNALGUI
}
#endif /*EXTERNALGUI*/
redraw_status = 1;
/* Wait for system to finish coming up */
while (!initdone) sleep(1);
/* Start up the RC file processing thread */
create_thread(&rctid,&sysblk.detattr,process_rc_file,NULL);
/* Process messages and commands */
while (1)
{
/* Set target CPU for commands and displays */
regs = sysblk.regs + sysblk.pcpu;
/* If the requested CPU is offline, then take the first available CPU*/
if(!regs->cpuonline)
/* regs = first online CPU
* sysblk.pcpu = number of online CPUs
*/
for(regs = 0, sysblk.pcpu = 0, i = 0 ;
i < MAX_CPU_ENGINES ; ++i )
if (sysblk.regs[i].cpuonline) {
if (!regs)
regs = sysblk.regs + i;
++sysblk.pcpu;
}
if (!regs) regs = sysblk.regs;
#if defined(_FEATURE_SIE)
/* Point to SIE copy in SIE state */
if(regs->sie_active)
regs = regs->guestregs;
#endif /*defined(_FEATURE_SIE)*/
/* Set the file descriptors for select */
FD_ZERO (&readset);
FD_SET (keybfd, &readset);
FD_SET (pipefd, &readset);
maxfd = keybfd;
if (pipefd > maxfd) maxfd = pipefd;
maxfd = add_socket_devices_to_fd_set (&readset, maxfd);
/* Wait for a message to arrive, a key to be pressed,
or the inactivity interval to expire */
tv.tv_sec = sysblk.panrate / 1000;
tv.tv_usec = (sysblk.panrate * 1000) % 1000000;
rc = select (maxfd + 1, &readset, NULL, NULL, &tv);
if (rc < 0 )
{
if (errno == EINTR) continue;
fprintf (stderr,
_("HHCPN004E select: %s\n"),
strerror(errno));
break;
}
/* If keyboard input has arrived then process it */
if (FD_ISSET(keybfd, &readset))
{
/* Read character(s) from the keyboard */
kblen = read (keybfd, kbbuf, kbbufsize-1);
if (kblen < 0)
{
fprintf (stderr,
_("HHCPN005E keyboard read: %s\n"),
strerror(errno));
break;
}
kbbuf[kblen] = '\0';
/* =NP= : Intercept NP commands & process */
if (NPDup == 1) {
if (NPdevsel == 1) { /* We are in device select mode */
NPdevsel = 0;
NPdevice = kbbuf[0]; /* save the device selected */
kbbuf[0] = NPsel2; /* setup for 2nd part of rtn */
}
if (NPdataentry == 0 && kblen == 1) { /* We are in command mode */
if (NPhelpup == 1) {
if (kbbuf[0] == 0x1b)
NPhelpdown = 1;
kbbuf[0] = '\0';
redraw_status = 1;
}
switch(kbbuf[0]) {
case 0x1b: /* ESC */
NPDup = 0;
break;
case '?':
NPhelpup = 1;
redraw_status = 1;
break;
case 'S': /* START */
case 's':
ASYNCHRONOUS_PANEL_CMD("start");
break;
case 'P': /* STOP */
case 'p':
ASYNCHRONOUS_PANEL_CMD("stop");
break;
case 'T': /* RESTART */
case 't':
ASYNCHRONOUS_PANEL_CMD("restart");
break;
case 'E': /* Ext int */
case 'e':
ASYNCHRONOUS_PANEL_CMD("ext");
redraw_status = 1;
break;
case 'O': /* Store */
case 'o':
NPaaddr = APPLY_PREFIXING (NPaddress, regs->PX);
if (NPaaddr > regs->mainlim)
break;
regs->mainstor[NPaaddr] = 0;
regs->mainstor[NPaaddr++] |= ((NPdata >> 24) & 0xFF);
regs->mainstor[NPaaddr] = 0;
regs->mainstor[NPaaddr++] |= ((NPdata >> 16) & 0xFF);
regs->mainstor[NPaaddr] = 0;
regs->mainstor[NPaaddr++] |= ((NPdata >> 8) & 0xFF);
regs->mainstor[NPaaddr] = 0;
regs->mainstor[NPaaddr++] |= ((NPdata) & 0xFF);
redraw_status = 1;
break;
case 'I': /* Display */
case 'i':
NPregdisp = 4;
redraw_status = 1;
break;
case 'g': /* display GPR */
case 'G':
NPregdisp = 0;
redraw_status = 1;
break;
case 'a': /* Display AR */
case 'A':
NPregdisp = 2;
redraw_status = 1;
break;
case 'c':
case 'C': /* Case CR */
NPregdisp = 1;
redraw_status = 1;
break;
case 'f': /* Case FPR */
case 'F':
NPregdisp = 3;
redraw_status = 1;
break;
case 'r': /* Enter address */
case 'R':
NPdataentry = 1;
NPpending = 'r';
NPcurpos[0] = 16;
NPcurpos[1] = 11;
NPdatalen = 8;
strcpy(NPcolor, ANSI_WHT_BLU);
strcpy(NPentered, "");
strcpy(NPprompt1, "Enter Address Switches");
redraw_status = 1;
break;
case 'd': /* Enter data */
case 'D':
NPdataentry = 1;
NPpending = 'd';
NPcurpos[0] = 16;
NPcurpos[1] = 29;
NPdatalen = 8;
strcpy(NPcolor, ANSI_WHT_BLU);
strcpy(NPentered, "");
strcpy(NPprompt1, "Enter Data Switches");
redraw_status = 1;
break;
case 'l': /* IPL */
case 'L':
NPdevsel = 1;
NPsel2 = 1;
strcpy(NPprompt2, "Select Device for IPL");
redraw_status = 1;
break;
case 1: /* IPL - 2nd part */
i = (NPdevice & 0x1F) - 1;
if (i < 0 || i > NPlastdev) {
strcpy(NPprompt2, "");
redraw_status = 1;
break;
}
sprintf(NPdevstr, "%x", NPdevaddr[i]);
strcpy(cmdline, "ipl ");
strcat(cmdline, NPdevstr);
ASYNCHRONOUS_PANEL_CMD(cmdline);
strcpy(NPprompt2, "");
redraw_status = 1;
break;
case 'u': /* Device interrupt */
case 'U':
NPdevsel = 1;
NPsel2 = 2;
strcpy(NPprompt2, "Select Device for Interrupt");
redraw_status = 1;
break;
case 2: /* Device int: part 2 */
i = (NPdevice & 0x1F) - 1;
if (i < 0 || i > NPlastdev) {
strcpy(NPprompt2, "");
redraw_status = 1;
break;
}
sprintf(NPdevstr, "%x", NPdevaddr[i]);
strcpy(cmdline, "i ");
strcat(cmdline, NPdevstr);
ASYNCHRONOUS_PANEL_CMD(cmdline);
strcpy(NPprompt2, "");
redraw_status = 1;
break;
case 'n': /* Device Assignment */
case 'N':
NPdevsel = 1;
NPsel2 = 3;
strcpy(NPprompt2, "Select Device to Reassign");
redraw_status = 1;
break;
case 3: /* Device asgn: part 2 */
i = NPasgn = (NPdevice & 0x1F) - 1;
if (i < 0 || i > NPlastdev) {
strcpy(NPprompt2, "");
redraw_status = 1;
break;
}
NPdataentry = 1;
NPpending = 'n';
NPcurpos[0] = 3 + i;
NPcurpos[1] = 57;
NPdatalen = 24;
strcpy(NPcolor, ANSI_WHT_BLU);
strcpy(NPentered, "");
strcpy(NPprompt2, "New Name, or [enter] to Reload");
redraw_status = 1;
break;
case 'W': /* POWER */
case 'w':
NPdevsel = 1;
NPsel2 = 4;
strcpy(NPprompt1, "Confirm Powerdown Y or N");
redraw_status = 1;
break;
case 4: /* IPL - 2nd part */
if (NPdevice == 'y' || NPdevice == 'Y')
ASYNCHRONOUS_PANEL_CMD("quit");
strcpy(NPprompt1, "");
redraw_status = 1;
break;
default:
break;
}
NPcmd = 1;
} else { /* We are in data entry mode */
NPcmd = 0;
}
if (NPcmd == 1)
kblen = 0; /* don't process as command */
}
/* =END= */
/* Process characters in the keyboard buffer */
for (i = 0; i < kblen; )
{
/* Test for home command */
if (strcmp(kbbuf+i, KBD_HOME) == 0)
{
if (firstmsgn == 0) break;
firstmsgn = 0;
redraw_msgs = 1;
break;
}
/* Test for end command */
if (strcmp(kbbuf+i, KBD_END) == 0)
{
if (firstmsgn + NUM_LINES >= nummsgs) break;
firstmsgn = nummsgs - NUM_LINES;
redraw_msgs = 1;
break;
}
/* Test for line up command */
if (strcmp(kbbuf+i, KBD_UP_ARROW) == 0)
{
if (firstmsgn == 0) break;
firstmsgn--;
redraw_msgs = 1;
break;
}
/* Test for line down command */
if (strcmp(kbbuf+i, KBD_DOWN_ARROW) == 0)
{
if (firstmsgn + NUM_LINES >= nummsgs) break;
firstmsgn++;
redraw_msgs = 1;
break;
}
/* Test for page up command */
if (strcmp(kbbuf+i, KBD_PAGE_UP) == 0)
{
if (firstmsgn == 0) break;
firstmsgn -= NUM_LINES;
if (firstmsgn < 0) firstmsgn = 0;
redraw_msgs = 1;
break;
}
/* Test for page down command */
if (strcmp(kbbuf+i, KBD_PAGE_DOWN) == 0)
{
if (firstmsgn + NUM_LINES >= nummsgs) break;
firstmsgn += NUM_LINES;
if (firstmsgn > nummsgs - NUM_LINES)
firstmsgn = nummsgs - NUM_LINES;
redraw_msgs = 1;
break;
}
/* Process escape key */
if (kbbuf[i] == '\x1B')
{
/* =NP= : Switch to new panel display */
NP_init();
NPDup = 1;
/* =END= */
break;
}
/* Process backspace character */
if (kbbuf[i] == '\b' || kbbuf[i] == '\x7F'
|| strcmp(kbbuf+i, KBD_LEFT_ARROW) == 0)
{
if (cmdoff > 0) cmdoff--;
i++;
redraw_cmd = 1;
break;
}
/* Process the command if newline was read */
if (kbbuf[i] == '\n')
{
cmdline[cmdoff] = '\0';
/* =NP= create_thread replaced with: */
if (NPDup == 0) {
#ifdef EXTERNALGUI
if (extgui && (cmdline[0] == ']'))
{
redraw_status = 1;
if (strncmp(cmdline,"]GREGS=",7) == 0)
{
gui_gregs = atoi(cmdline+7);
}
else
if (strncmp(cmdline,"]CREGS=",7) == 0)
{
gui_cregs = atoi(cmdline+7);
}
else
if (strncmp(cmdline,"]AREGS=",7) == 0)
{
gui_aregs = atoi(cmdline+7);
}
else
if (strncmp(cmdline,"]FREGS=",7) == 0)
{
gui_fregs = atoi(cmdline+7);
}
else
if (strncmp(cmdline,"]DEVLIST=",9) == 0)
{
gui_devlist = atoi(cmdline+9);
}
else
if (strncmp(cmdline,"]MAINSTOR=",10) == 0)
{
fprintf(stderr,"MAINSTOR=%d\n",(U32)regs->mainstor);
fprintf(stderr,"MAINSIZE=%d\n",(U32)sysblk.mainsize);
}
#if defined(OPTION_MIPS_COUNTING)
else
if (strncmp(cmdline,"]CPUPCT=",8) == 0)
{
gui_cpupct = atoi(cmdline+8);
}
#endif /*defined(OPTION_MIPS_COUNTING)*/
}
else
#endif /*EXTERNALGUI*/
{
if ('#' == cmdline[0] || '*' == cmdline[0])
{
if ('*' == cmdline[0])
logmsg("%s\n", cmdline);
}
else
{
if (rc_thread_done)
{
ASYNCHRONOUS_PANEL_CMD(cmdline);
}
}
}
} else {
NPdataentry = 0;
NPcurpos[0] = 24;
NPcurpos[1] = 80;
strcpy(NPcolor, "");
switch (NPpending) {
case 'r':
sscanf(cmdline, "%x", &NPaddress);
NPcuraddr = -1;
strcpy(NPprompt1, "");
break;
case 'd':
sscanf(cmdline, "%x", &NPdata);
NPcurdata = -1;
strcpy(NPprompt1, "");
break;
case 'n':
if (strlen(cmdline) < 1) {
strcpy(cmdline, NPdevname[NPasgn]);
}
strcpy(NPdevname[NPasgn], "");
strcpy(NPentered, "devinit ");
sprintf(NPdevstr, "%x", NPdevaddr[NPasgn]);
strcat(NPentered, NPdevstr);
strcat(NPentered, " ");
strcat(NPentered, cmdline);
ASYNCHRONOUS_PANEL_CMD(NPentered);
strcpy(NPprompt2, "");
break;
default:
break;
}
redraw_status = 1;
}
/* =END= */
cmdoff = 0;
#ifdef EXTERNALGUI
/* Process *ALL* of the 'keyboard' (stdin) buffer data! */
if (extgui) {i++; continue;}
#endif /*EXTERNALGUI*/
redraw_cmd = 1;
break;
}
/* Ignore non-printable characters */
if (!isprint(kbbuf[i]))
{
logmsg ("%2.2X\n", kbbuf[i]);
i++;
continue;
}
/* Append the character to the command buffer */
if (cmdoff < CMD_SIZE-1) cmdline[cmdoff++] = kbbuf[i];
i++;
redraw_cmd = 1;
} /* end for(i) */
}
/* If a message has arrived then receive it */
if (FD_ISSET(pipefd, &readset))
{
/* Clear the message buffer */
memset (readbuf, SPACE, MSG_SIZE);
/* Read message bytes until newline */
while (1)
{
/* Read a byte from the message pipe */
rc = read (pipefd, &c, 1);
if (rc < 1)
{
fprintf (stderr,
"HHCPN006E message pipe read: %s\n",
strerror(errno));
break;
}
/* Exit if newline was read */
if (c == '\n') break;
/* Handle tab character */
if (c == '\t')
{
readoff += 8;
readoff &= 0xFFFFFFF8;
continue;
}
/* Eliminate non-printable characters */
if (!isprint(c)) c = SPACE;
/* Append the byte to the read buffer */
if (readoff < MSG_SIZE) readbuf[readoff++] = c;
} /* end while */
/* Exit if read was unsuccessful */
if (rc < 1) break;
/* Copy the message to the log file if present */
if (logfp != NULL)
{
fprintf (logfp, "%.*s\n", readoff, readbuf);
if (ferror(logfp))
{
fclose (logfp);
logfp = NULL;
}
}
/* Copy message to circular buffer and empty read buffer */
#if defined(EXTERNALGUI) && !defined(OPTION_HTTP_SERVER)
if (!extgui)
#endif /*EXTERNALGUI*/
memcpy (msgbuf + (msgslot * MSG_SIZE), readbuf, MSG_SIZE);
readoff = 0;
#if defined(EXTERNALGUI) && !defined(OPTION_HTTP_SERVER)
if (!extgui)
{
#endif /*EXTERNALGUI*/
/* Update message count and next available slot number */
if (nummsgs < MAX_MSGS)
msgslot = ++nummsgs;
else
msgslot++;
if (msgslot == MAX_MSGS) msgslot = 0;
/* Calculate the first line to display */
firstmsgn = nummsgs - NUM_LINES;
if (firstmsgn < 0) firstmsgn = 0;
/* Set the display update indicator */
redraw_msgs = 1;
#if defined(EXTERNALGUI) && !defined(OPTION_HTTP_SERVER)
}
#endif /*EXTERNALGUI*/
}
/* Check if any sockets have received new connections */
check_socket_devices_for_connections (&readset);
#ifdef EXTERNALGUI
if (!extgui)
#endif /*EXTERNALGUI*/
/* =NP= : Reinit traditional panel if NP is down */
if (NPDup == 0 && NPDinit == 1) {
NPDinit = 0;
redraw_msgs = 1;
redraw_status = 1;
redraw_cmd = 1;
fprintf(confp, ANSI_WHT_BLK);
fprintf(confp, ANSI_ERASE_SCREEN);
}
/* =END= */
/* Obtain the PSW for target CPU */
memset (curpsw, 0x00, sizeof(curpsw));
store_psw (regs, curpsw);
/* Isolate the PSW interruption wait bit */
pswwait = curpsw[1] & 0x02;
/* Set the display update indicator if the PSW has changed
or if the instruction counter has changed, or if
the CPU stopped state has changed */
if (memcmp(curpsw, prvpsw, sizeof(curpsw)) != 0 || (
#if defined(_FEATURE_SIE)
regs->sie_state ? regs->hostregs->instcount :
#endif /*defined(_FEATURE_SIE)*/
regs->instcount) != prvicount
|| regs->cpustate != prvstate)
{
redraw_status = 1;
memcpy (prvpsw, curpsw, sizeof(prvpsw));
prvicount =
#if defined(_FEATURE_SIE)
regs->sie_state ? regs->hostregs->instcount :
#endif /*defined(_FEATURE_SIE)*/
regs->instcount;
prvstate = regs->cpustate;
}
/* =NP= : Display the screen - traditional or NP */
/* Note: this is the only code block modified rather */
/* than inserted. It makes the block of 3 ifs in the */
/* original code dependent on NPDup == 0, and inserts */
/* the NP display as an else after those ifs */
if (NPDup == 0) {
#ifdef EXTERNALGUI
if (!extgui)
#endif /*EXTERNALGUI*/
/* Rewrite the screen if display update indicators are set */
if (redraw_msgs && !sysblk.npquiet)
{
/* Display messages in scrolling area */
for (i=0; i < NUM_LINES && firstmsgn + i < nummsgs; i++)
{
n = (nummsgs < MAX_MSGS) ? 0 : msgslot;
n += firstmsgn + i;
if (n >= MAX_MSGS) n -= MAX_MSGS;
fprintf (confp,
ANSI_POSITION_CURSOR
ANSI_WHITE_BLACK,
i+1, 1);
fwrite (msgbuf + (n * MSG_SIZE), MSG_SIZE, 1, confp);
}
/* Display the scroll indicators */
if (firstmsgn > 0)
fprintf (confp, ANSI_ROW1_COL80 "+");
if (firstmsgn + i < nummsgs)
fprintf (confp, ANSI_ROW22_COL80 "V");
} /* end if(redraw_msgs) */
if (redraw_status && !sysblk.npquiet)
{
/* Display the PSW and instruction counter for CPU 0 */
fprintf (confp,
"%s"
#if MAX_CPU_ENGINES > 1
"CPU%4.4X "
#endif
"PSW=%2.2X%2.2X%2.2X%2.2X %2.2X%2.2X%2.2X%2.2X"
" %2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X"
" %c%c%c%c%c%c%c%c instcount=%llu"
"%s",
#ifdef EXTERNALGUI
extgui ? ("STATUS=") :
#endif /*EXTERNALGUI*/
(ANSI_ROW24_COL1 ANSI_YELLOW_RED),
#if MAX_CPU_ENGINES > 1
regs->cpuad,
#endif
curpsw[0], curpsw[1], curpsw[2], curpsw[3],
curpsw[4], curpsw[5], curpsw[6], curpsw[7],
curpsw[8], curpsw[9], curpsw[10], curpsw[11],
curpsw[12], curpsw[13], curpsw[14], curpsw[15],
regs->cpustate == CPUSTATE_STOPPED ? 'M' : '.',
sysblk.inststep ? 'T' : '.',
pswwait ? 'W' : '.',
regs->loadstate ? 'L' : '.',
regs->checkstop ? 'C' : '.',
regs->psw.prob ? 'P' : '.',
#if defined(_FEATURE_SIE)
regs->sie_state ? 'S' : '.',
#else /*!defined(_FEATURE_SIE)*/
'.',
#endif /*!defined(_FEATURE_SIE)*/
#if defined(_900)
regs->arch_mode == ARCH_900 ? 'Z' : '.',
#else
' ',
#endif
#if defined(_FEATURE_SIE)
regs->sie_state ? (long long) regs->hostregs->instcount :
#endif /*defined(_FEATURE_SIE)*/
(long long)regs->instcount,
#ifdef EXTERNALGUI
extgui ? "\n" :
#endif /*EXTERNALGUI*/
ANSI_ERASE_EOL);
#ifdef EXTERNALGUI
if (extgui)
{
/* SYS / WAIT lights */
if (!(regs->cpustate == CPUSTATE_STOPPING ||
regs->cpustate == CPUSTATE_STOPPED))
fprintf(confp,"SYS=%c\n",pswwait?'0':'1');
#ifdef OPTION_MIPS_COUNTING
/* Calculate MIPS rate */
#ifdef FEATURE_CPU_RECONFIG
for (mipsrate = siosrate = i = 0; i < MAX_CPU_ENGINES; i++)
if(sysblk.regs[i].cpuonline)
#else /*!FEATURE_CPU_RECONFIG*/
for(mipsrate = siosrate = i = 0; i < sysblk.numcpu; i++)
#endif /*!FEATURE_CPU_RECONFIG*/
{
mipsrate += sysblk.regs[i].mipsrate;
siosrate += sysblk.regs[i].siosrate;
}
if (mipsrate > 100000) mipsrate = 0; /* ignore wildly high rate */
/* MIPS rate */
if (prevmipsrate != mipsrate)
{
prevmipsrate = mipsrate;
fprintf(confp, "MIPS=%2.1d.%2.2d\n",
prevmipsrate / 1000, (prevmipsrate % 1000) / 10);
}
/* SIO rate */
if (prevsiosrate != siosrate)
{
prevsiosrate = siosrate;
fprintf(confp, "SIOS=%5d\n",prevsiosrate);
}
#endif /*OPTION_MIPS_COUNTING*/
if (gui_gregs) /* GP regs */
{
fprintf(confp,"GR0-3=%8.8X %8.8X %8.8X %8.8X\n",
regs->GR_L(0),regs->GR_L(1),regs->GR_L(2),regs->GR_L(3));
fprintf(confp,"GR4-7=%8.8X %8.8X %8.8X %8.8X\n",
regs->GR_L(4),regs->GR_L(5),regs->GR_L(6),regs->GR_L(7));
fprintf(confp,"GR8-B=%8.8X %8.8X %8.8X %8.8X\n",
regs->GR_L(8),regs->GR_L(9),regs->GR_L(10),regs->GR_L(11));
fprintf(confp,"GRC-F=%8.8X %8.8X %8.8X %8.8X\n",
regs->GR_L(12),regs->GR_L(13),regs->GR_L(14),regs->GR_L(15));
}
if (gui_cregs) /* CR regs */
{
fprintf(confp,"CR0-3=%8.8X %8.8X %8.8X %8.8X\n",
regs->CR_L(0),regs->CR_L(1),regs->CR_L(2),regs->CR_L(3));
fprintf(confp,"CR4-7=%8.8X %8.8X %8.8X %8.8X\n",
regs->CR_L(4),regs->CR_L(5),regs->CR_L(6),regs->CR_L(7));
fprintf(confp,"CR8-B=%8.8X %8.8X %8.8X %8.8X\n",
regs->CR_L(8),regs->CR_L(9),regs->CR_L(10),regs->CR_L(11));
fprintf(confp,"CRC-F=%8.8X %8.8X %8.8X %8.8X\n",
regs->CR_L(12),regs->CR_L(13),regs->CR_L(14),regs->CR_L(15));
}
if (gui_aregs) /* AR regs */
{
fprintf(confp,"AR0-3=%8.8X %8.8X %8.8X %8.8X\n",
regs->AR(0),regs->AR(1),regs->AR(2),regs->AR(3));
fprintf(confp,"AR4-7=%8.8X %8.8X %8.8X %8.8X\n",
regs->AR(4),regs->AR(5),regs->AR(6),regs->AR(7));
fprintf(confp,"AR8-B=%8.8X %8.8X %8.8X %8.8X\n",
regs->AR(8),regs->AR(9),regs->AR(10),regs->AR(11));
fprintf(confp,"ARC-F=%8.8X %8.8X %8.8X %8.8X\n",
regs->AR(12),regs->AR(13),regs->AR(14),regs->AR(15));
}
if (gui_fregs) /* FP regs */
{
fprintf(confp,"FR0-2=%8.8X %8.8X %8.8X %8.8X\n",
regs->fpr[0],regs->fpr[1],regs->fpr[2],regs->fpr[3]);
fprintf(confp,"FR4-6=%8.8X %8.8X %8.8X %8.8X\n",
regs->fpr[4],regs->fpr[5],regs->fpr[6],regs->fpr[7]);
}
#if defined(OPTION_MIPS_COUNTING)
if (gui_cpupct) /* CPU Utilization */
fprintf(confp,"CPUPCT=%d\n",(int)(100.0 * regs->cpupct));
#endif /*defined(OPTION_MIPS_COUNTING)*/
if (gui_devlist) /* device status */
gui_devlist_status (confp);
}
#endif /*EXTERNALGUI*/
} /* end if(redraw_status) */
#ifdef EXTERNALGUI
else /* !redraw_status */
{
/* If we're under the control of an external GUI,
some status info we need to send ALL the time. */
if (extgui)
{
/* SYS / WAIT lights */
if (!(regs->cpustate == CPUSTATE_STOPPING ||
regs->cpustate == CPUSTATE_STOPPED))
fprintf(confp,"SYS=%c\n",pswwait?'0':'1');
#if defined(OPTION_MIPS_COUNTING)
if (gui_cpupct) /* CPU Utilization */
fprintf(confp,"CPUPCT=%d\n",(int)(100.0 * regs->cpupct));
#endif /*defined(OPTION_MIPS_COUNTING)*/
if (gui_devlist) /* device status */
gui_devlist_status (confp);
}
}
if (!extgui)
#endif /*EXTERNALGUI*/
if (redraw_cmd)
{
/* Display the command line */
fprintf (confp,
ANSI_ROW23_COL1
ANSI_WHITE_BLACK
ANSI_HIGH_INTENSITY
"Command ==> "
ANSI_WHITE_BLACK);
for (i = 0; i < cmdoff; i++)
putc (cmdline[i], confp);
fprintf (confp,
ANSI_ERASE_EOL);
} /* end if(redraw_cmd) */
/* Flush screen buffer and reset display update indicators */
if (redraw_msgs || redraw_cmd || redraw_status)
{
#ifdef EXTERNALGUI
if (!extgui)
#endif /*EXTERNALGUI*/
fprintf (confp,
ANSI_POSITION_CURSOR,
23, 13+cmdoff);
fflush (confp);
redraw_msgs = 0;
redraw_cmd = 0;
redraw_status = 0;
}
} else {
if (redraw_status || (NPDinit == 0 && NPDup == 1)
|| (redraw_cmd && NPdataentry == 1)) {
if (NPDinit == 0) {
NPDinit = 1;
#ifdef EXTERNALGUI
if (!extgui)
#endif /*EXTERNALGUI*/
NP_screen(confp);
}
#ifdef EXTERNALGUI
if (!extgui)
#endif /*EXTERNALGUI*/
NP_update(confp, cmdline, cmdoff);
fflush (confp);
redraw_msgs = 0;
redraw_cmd = 0;
redraw_status = 0;
}
}
/* =END= */
} /* end while */
return;
} /* end function panel_display */
#if defined(EXTERNALGUI)
void gui_devlist_status (FILE *confp)
{
DEVBLK *dev;
for (dev = sysblk.firstdev; dev != NULL; dev = dev->nextdev)
{
if (!(dev->pmcw.flag5 & PMCW5_V)) continue;
stat_online = stat_busy = stat_pend = stat_open = 0;
(dev->hnd->query)(dev, &devclass, sizeof(devnam), devnam);
devnam[255] = 0; /* (ensure null termination) */
if ((dev->console && dev->connected) ||
(strlen(dev->filename) > 0))
stat_online = 1;
if (dev->busy) stat_busy = 1;
if (dev->pending) stat_pend = 1;
if (dev->fd > 2) stat_open = 1;
fprintf(confp, "DEV=%4.4X %4.4X %-4.4s %d%d%d%d %s\n",
dev->devnum,
dev->devtype,
devclass,
stat_online,
stat_busy,
stat_pend,
stat_open,
devnam);
}
fprintf(confp, "DEV=X\n"); /* (indicate end of list) */
}
#endif /*defined(EXTERNALGUI)*/
/*===================================================================*/
/* S o c k e t D e v i c e s ... */
/*===================================================================*/
// #define DEBUG_SOCKDEV
#ifdef DEBUG_SOCKDEV
#define logdebug(args...) logmsg(## args)
#else
#define logdebug(args...) do {} while (0)
#endif /* DEBUG_SOCKDEV */
/* Linked list of bind structures for bound socket devices */
LIST_ENTRY bind_head; /* (bind_struct list anchor) */
LOCK bind_lock; /* (lock for accessing list) */
/*-------------------------------------------------------------------*/
/* bind_device bind a device to a socket (adds entry to our list */
/* of bound devices) (1=success, 0=failure) */
/*-------------------------------------------------------------------*/
int bind_device (DEVBLK* dev, char* spec)
{
bind_struct* bs;
logdebug("bind_device (%4.4X, %s)\n", dev->devnum, spec);
/* Error if device already bound */
if (dev->bs)
{
logmsg (_("HHC416I Device %4.4X already bound to socket %s\n"),
dev->devnum, dev->bs->spec);
return 0; /* (failure) */
}
/* Create a new bind_struct entry */
bs = malloc(sizeof(bind_struct));
if (!bs)
{
logmsg (_("HHC415I bind_device malloc() failed for device %4.4X\n"),
dev->devnum);
return 0; /* (failure) */
}
memset(bs,0,sizeof(bind_struct));
if (!(bs->spec = safe_strdup(spec)))
{
logmsg (_("HHC415I bind_device safe_strdup() failed for device %4.4X\n"),
dev->devnum);
free (bs);
return 0; /* (failure) */
}
/* Create a listening socket */
if (bs->spec[0] == '/') bs->sd = unix_socket (bs->spec);
else bs->sd = inet_socket (bs->spec);
if (bs->sd == -1)
{
/* (error message already issued) */
free (bs);
return 0; /* (failure) */
}
/* Chain device and socket to each other */
dev->bs = bs;
bs->dev = dev;
/* Add the new entry to our list of bound devices */
obtain_lock(&bind_lock);
InsertListTail(&bind_head,&bs->bind_link);
release_lock(&bind_lock);
logmsg (_("HHC416I Device %4.4X bound to socket %s\n"),
dev->devnum, dev->bs->spec);
return 1; /* (success) */
}
/*-------------------------------------------------------------------*/
/* unbind_device unbind a device from a socket (removes entry from */
/* our list and discards it) (1=success, 0=failure) */
/*-------------------------------------------------------------------*/
int unbind_device (DEVBLK* dev)
{
bind_struct* bs;
logdebug("unbind_device(%4.4X)\n", dev->devnum);
/* Error if device not bound */
if (!(bs = dev->bs))
{
logmsg (_("HHC416I Device %4.4X not bound to any socket\n"),
dev->devnum);
return 0; /* (failure) */
}
/* Error if someone still connected */
if (dev->fd != -1)
{
logmsg (_("HHC416I Client %s (%s) still connected to device %4.4X (%s)\n"),
dev->bs->clientip, dev->bs->clientname, dev->devnum, dev->bs->spec);
return 0; /* (failure) */
}
/* Remove the entry from our list */
obtain_lock(&bind_lock);
RemoveListEntry(&bs->bind_link);
release_lock(&bind_lock);
/* Unchain device and socket from each another */
dev->bs = NULL;
bs->dev = NULL;
/* Close the listening socket */
if (bs->sd != -1)
close (bs->sd);
logmsg (_("HHC422I Device %4.4X unbound from socket %s\n"),
dev->devnum, bs->spec);
/* Discard the entry */
if (bs->clientname)
free(bs->clientname);
bs->clientname = NULL;
if (bs->clientip)
free(bs->clientip);
bs->clientip = NULL;
free (bs->spec);
free (bs);
return 1; /* (success) */
}
/*-------------------------------------------------------------------*/
/* unix_socket create and bind a Unix domain socket */
/*-------------------------------------------------------------------*/
#include <sys/un.h> /* (need "sockaddr_un") */
int unix_socket (char* path)
{
struct sockaddr_un addr;
int sd;
logdebug ("unix_socket(%s)\n", path);
if (strlen (path) > sizeof(addr.sun_path) - 1)
{
logmsg (_("HHC411I Socket pathname \"%s\" exceeds limit of %d\n"),
path, (int) sizeof(addr.sun_path) - 1);
return -1;
}
addr.sun_family = AF_UNIX;
strcpy (addr.sun_path, path); /* guaranteed room by above check */
sd = socket (PF_UNIX, SOCK_STREAM, 0);
if (sd == -1)
{
logmsg (_("HHC412I Error creating socket for %s: %s\n"),
path, strerror(errno));
return -1;
}
unlink (path);
fchmod (sd, 0700);
if (0
|| bind (sd, (struct sockaddr*) &addr, sizeof(addr)) == -1
|| listen (sd, 5) == -1
)
{
logmsg (_("HHC413I Failed to bind or listen on socket %s: %s\n"),
path, strerror(errno));
return -1;
}
return sd;
}
/*-------------------------------------------------------------------*/
/* inet_socket create and bind a regular TCP/IP socket */
/*-------------------------------------------------------------------*/
int inet_socket (char* spec)
{
/* We need a copy of the path to overwrite a ':' with '\0' */
char buf[sizeof(((DEVBLK*)0)->filename)];
char* colon;
char* node;
char* service;
int sd;
int one = 1;
struct sockaddr_in sin;
logdebug("inet_socket(%s)\n", spec);
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
strcpy(buf, spec);
colon = strchr(buf, ':');
if (colon)
{
*colon = '\0';
node = buf;
service = colon + 1;
}
else
{
node = NULL;
service = buf;
}
if (!node)
sin.sin_addr.s_addr = INADDR_ANY;
else
{
struct hostent* he = gethostbyname(node);
if (!he)
{
logmsg (_("HHC414I Failed to determine IP address from %s\n"),
node);
return -1;
}
memcpy(&sin.sin_addr, he->h_addr_list[0], sizeof(sin.sin_addr));
}
if (isdigit(service[0]))
{
sin.sin_port = htons(atoi(service));
}
else
{
struct servent* se = getservbyname(service, "tcp");
if (!se)
{
logmsg (_("HHC417I Failed to determine port number from %s\n"),
service);
return -1;
}
sin.sin_port = se->s_port;
}
sd = socket (PF_INET, SOCK_STREAM, 0);
if (sd == -1)
{
logmsg (_("HHC412I Error creating socket for %s: %s\n"),
spec, strerror(errno));
return -1;
}
setsockopt (sd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
if (0
|| bind (sd, (struct sockaddr*) &sin, sizeof(sin)) == -1
|| listen (sd, 5) == -1
)
{
logmsg (_("HHC413I Failed to bind or listen on socket %s: %s\n"),
spec, strerror(errno));
return -1;
}
return sd;
}
/*-------------------------------------------------------------------*/
/* add_socket_devices_to_fd_set add all bound socket devices' */
/* listening sockets to the FD_SET */
/*-------------------------------------------------------------------*/
int add_socket_devices_to_fd_set (fd_set* readset, int maxfd)
{
DEVBLK* dev;
bind_struct* bs;
LIST_ENTRY* pListEntry;
obtain_lock(&bind_lock);
pListEntry = bind_head.Flink;
while (pListEntry != &bind_head)
{
bs = CONTAINING_RECORD(pListEntry,bind_struct,bind_link);
if (bs->sd != -1) /* if listening for connections, */
{
dev = bs->dev;
if (dev->fd == -1) /* and not already connected, */
{
FD_SET(bs->sd, readset); /* then add file to set */
if (bs->sd > maxfd)
maxfd = bs->sd;
}
}
pListEntry = pListEntry->Flink;
}
release_lock(&bind_lock);
return maxfd;
}
/*-------------------------------------------------------------------*/
/* check_socket_devices_for_connections */
/*-------------------------------------------------------------------*/
void check_socket_devices_for_connections (fd_set* readset)
{
bind_struct* bs;
LIST_ENTRY* pListEntry;
obtain_lock(&bind_lock);
pListEntry = bind_head.Flink;
while (pListEntry != &bind_head)
{
bs = CONTAINING_RECORD(pListEntry,bind_struct,bind_link);
if (bs->sd != -1 && FD_ISSET(bs->sd, readset))
{
/* Note: there may be other connection requests
* waiting to be serviced, but we'll catch them
* the next time the panel thread calls us. */
release_lock(&bind_lock);
socket_device_connection_handler(bs);
return;
}
pListEntry = pListEntry->Flink;
}
release_lock(&bind_lock);
}
/*-------------------------------------------------------------------*/
/* socket_device_connection_handler */
/*-------------------------------------------------------------------*/
void socket_device_connection_handler (bind_struct* bs)
{
struct sockaddr_in client; /* Client address structure */
struct hostent* pHE; /* Addr of hostent structure */
socklen_t namelen; /* Length of client structure*/
char* clientip; /* Addr of client ip address */
char* clientname; /* Addr of client hostname */
DEVBLK* dev; /* Device Block pointer */
int csock; /* Client socket */
dev = bs->dev;
logdebug("socket_device_connection_handler(dev=%4.4X)\n",
dev->devnum);
/* Obtain the device lock */
obtain_lock (&dev->lock);
/* Reject if device is busy or interrupt pending */
if (dev->busy || dev->pending || (dev->scsw.flag3 & SCSW3_SC_PEND))
{
release_lock (&dev->lock);
logmsg (_("HHC418I Connect to device %4.4X (%s) rejected; "
"device busy or interrupt pending\n"),
dev->devnum, bs->spec);
return;
}
/* Reject if previous connection not closed (should not occur) */
if (dev->fd != -1)
{
release_lock (&dev->lock);
logmsg (_("HHC418I Connect to device %4.4X (%s) rejected; "
"client %s (%s) still connected\n"),
dev->devnum, bs->spec, bs->clientip, bs->clientname);
return;
}
/* Accept the connection... */
csock = accept(bs->sd, 0, 0);
if (csock == -1)
{
release_lock (&dev->lock);
logmsg (_("HHC418I Connect to device %4.4X (%s) failed: %s\n"),
dev->devnum, bs->spec, strerror(errno));
return;
}
/* Determine the connected client's IP address and hostname */
namelen = sizeof(client);
clientip = NULL;
clientname = "host name unknown";
if (1
&& getpeername(csock, (struct sockaddr*) &client, &namelen) == 0
&& (clientip = inet_ntoa(client.sin_addr)) != NULL
&& (pHE = gethostbyaddr((unsigned char*)(&client.sin_addr),
sizeof(client.sin_addr), AF_INET)) != NULL
&& pHE->h_name && *pHE->h_name
)
{
clientname = (char*) pHE->h_name;
}
/* Log the connection */
if (clientip)
{
logmsg (_("HHC420I %s (%s) connected to device %4.4X (%s)\n"),
clientip, clientname, dev->devnum, bs->spec);
}
else
{
logmsg (_("HHC420I <unknown> connected to device %4.4X (%s)\n"),
dev->devnum, bs->spec);
}
/* Save the connected client information in the bind_struct */
if (bs->clientip) free(bs->clientip);
if (bs->clientname) free(bs->clientname);
bs->clientip = safe_strdup(clientip);
bs->clientname = safe_strdup(clientname);
/* Indicate that a client is now connected to device (prevents
* listening for new connections until THIS client disconnects).
*/
dev->fd = csock; /* (indicate client connected to device) */
/* Release the device lock */
release_lock (&dev->lock);
/* Raise unsolicited device end interrupt for the device */
device_attention (dev, CSW_DE);
}
/*-------------------------------------------------------------------*/
/* safe_strdup make copy of string and return a pointer to it */
/*-------------------------------------------------------------------*/
char* safe_strdup (char* str)
{
char* newstr;
if (!str) return NULL;
newstr = malloc (strlen (str) + 1);
if (!newstr) return NULL;
strcpy (newstr, str); /* (guaranteed room) */
return newstr;
}