Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request for TCP/IP (sockdev) functionality be added to punch devices. #553

Closed
wa6our opened this issue Apr 1, 2023 · 4 comments
Closed
Assignees
Labels
Enhancement This issue does not describe a problem but rather describes a suggested change or improvement.

Comments

@wa6our
Copy link

wa6our commented Apr 1, 2023

I have two Hercules images living on Oracle cloud. I have a program I wrote that connects a 3270 and Line printer to my local MAC via TCP/IP. I would like to do the same with the Punch. I am told that should be simple to do. I hope it is.

Thanks for a wonderful product.

@MockbaTheBorg
Copy link

@wa6our have you tried this?
attach 002d 3525 0.0.0.0:3525 ascii (from Hercules prompt)
or
002d 3525 0.0.0.0:3525 ascii (added to the hercules cfg)

This creates a socket for the punch, just like a printer. Works the same for punches, readers, printers.

Sorry if I misunderstood your request, but this is how I do it here on my "mainframe".

@MockbaTheBorg
Copy link

MockbaTheBorg commented Apr 2, 2023

Never mind, it doesn't seem to work, even though the configuration takes without errors. it just creates a 0.0.0.0 file.

I will leave my reply above here as an ode to my stupidity.

@Fish-Git Fish-Git added the Enhancement This issue does not describe a problem but rather describes a suggested change or improvement. label Apr 2, 2023
@Fish-Git Fish-Git self-assigned this Apr 3, 2023
@Fish-Git Fish-Git added the IN PROGRESS... I'm working on it! (Or someone else is!) label Apr 3, 2023
@Fish-Git Fish-Git changed the title Request that you add TCP/IP capability to the punch on Hercules. Request for TCP/IP (sockdev) functionality be added to punch devices. Apr 3, 2023
@Fish-Git
Copy link
Member

Fish-Git commented Apr 3, 2023

@wa6our? (what is your name by the way?)

Since you're running Hercules on a Mac, may I presume you are able to build the current development (git develop branch) version of Hercules?

I've made the needed changes (see patch below) but am scratching my head over how to test it without having to write my own punch spooler application. It would be faster and easier if you could apply the below patch to current development git and give it a try. Thanks.

--- hyperion-git/cardpch.c	2022-11-26 17:01:05.936039300 -0800
+++ hyperion-1/cardpch.c	2023-04-03 13:29:50.077871300 -0700
@@ -14,6 +14,7 @@
 #include "hercules.h"
 
 #include "devtype.h"
+#include "sockdev.h"
 
 /*-------------------------------------------------------------------*/
 /* Internal macro definitions                                        */
@@ -29,23 +30,157 @@
 {
 int             rc;                     /* Return code               */
 
-    /* Write data to the output file */
-    rc = write (dev->fd, buf, len);
+    if (dev->bs)    /* socket device? */
+    {
+        /* Write data to socket, check for error */
+        if ((rc = write_socket( dev->fd, buf, len )) < len)
+        {
+            /* Close the connection */
+            if (dev->fd != -1)
+            {
+                int fd = dev->fd;
+                dev->fd = -1;
+                close_socket( fd );
+                // "%1d:%04X Card: client %s, IP %s disconnected from device %s"
+                WRMSG( HHC01211, "I", LCSS_DEVNUM, dev->bs->clientname, dev->bs->clientip, dev->bs->spec );
+            }
 
-    /* Equipment check if error writing to output file */
-    if (rc < len)
+            /* Set unit check with intervention required */
+            dev->sense[0] = SENSE_IR;
+            *unitstat = CSW_CE | CSW_DE | CSW_UC;
+        }
+    }
+    else
     {
-        // "%1d:%04X %s: error in function %s: %s"
-        WRMSG( HHC01250, "E", LCSS_DEVNUM,
-               "Card", "write()", errno == 0 ? "incomplete"
-                                             : strerror( errno ));
-        dev->sense[0] = SENSE_EC;
-        *unitstat = CSW_CE | CSW_DE | CSW_UC;
-        return;
+        /* Write data to the output file */
+        rc = write (dev->fd, buf, len);
+
+        /* Equipment check if error writing to output file */
+        if (rc < len)
+        {
+            // "%1d:%04X %s: error in function %s: %s"
+            WRMSG( HHC01250, "E", LCSS_DEVNUM,
+                   "Card", "write()", errno == 0 ? "incomplete"
+                                                 : strerror( errno ));
+            dev->sense[0] = SENSE_EC;
+            *unitstat = CSW_CE | CSW_DE | CSW_UC;
+        }
     }
 
 } /* end function write_buffer */
 
+/*-------------------------------------------------------------------*/
+/* Thread to monitor the sockdev remote punch spooler connection     */
+/*-------------------------------------------------------------------*/
+static void* spthread (void* arg)
+{
+    DEVBLK* dev = (DEVBLK*) arg;
+    BYTE byte;
+    fd_set readset, errorset;
+    struct timeval tv;
+    int rc, fd = dev->fd;           // (save original fd)
+
+    /* Fix thread name */
+    {
+        char    thread_name[16];
+        MSGBUF( thread_name, "spthread %1d:%04X", LCSS_DEVNUM );
+        SET_THREAD_NAME( thread_name );
+    }
+
+    // Looooop...  until shutdown or disconnect...
+
+    // PROGRAMMING NOTE: we do our select specifying an immediate
+    // timeout to prevent our select from holding up (slowing down)
+    // the device thread (which does the actual writing of data to
+    // the client). The only purpose for our thread even existing
+    // is to detect a severed connection (i.e. to detect when the
+    // client disconnects)...
+
+    while ( !sysblk.shutdown && dev->fd == fd )
+    {
+        if (dev->busy)
+        {
+            SLEEP(3);
+            continue;
+        }
+
+        FD_ZERO( &readset );
+        FD_ZERO( &errorset );
+
+        FD_SET( fd, &readset );
+        FD_SET( fd, &errorset );
+
+        tv.tv_sec = 0;
+        tv.tv_usec = 0;
+
+        rc = select( fd+1, &readset, NULL, &errorset, &tv );
+
+        if (rc < 0)
+            break;
+
+        if (rc == 0)
+        {
+            SLEEP(3);
+            continue;
+        }
+
+        if (FD_ISSET( fd, &errorset ))
+            break;
+
+        // Read and ignore any data they send us...
+        // Note: recv should complete immediately
+        // as we know data is waiting to be read.
+
+        ASSERT( FD_ISSET( fd, &readset ) );
+
+        rc = recv( fd, &byte, sizeof(byte), 0 );
+
+        if (rc <= 0)
+            break;
+    }
+
+    obtain_lock( &dev->lock );
+
+    // PROGRAMMING NOTE: the following tells us whether we detected
+    // the error or if the device thread already did. If the device
+    // thread detected it while we were sleeping (and subsequently
+    // closed the connection) then we don't need to do anything at
+    // all; just exit. If we were the ones that detected the error
+    // however, then we need to close the connection so the device
+    // thread can learn of it...
+
+    if (dev->fd == fd)
+    {
+        dev->fd = -1;
+        close_socket( fd );
+        // "%1d:%04X Card: client %s, IP %s disconnected from device %s"
+        WRMSG( HHC01211, "I", LCSS_DEVNUM, dev->bs->clientname, dev->bs->clientip, dev->bs->spec );
+    }
+
+    release_lock( &dev->lock );
+
+    return NULL;
+
+} /* end function spthread */
+
+/*-------------------------------------------------------------------*/
+/* Sockdev "OnConnection" callback function                          */
+/*-------------------------------------------------------------------*/
+static int onconnect_callback (DEVBLK* dev)
+{
+    TID tid;
+    int rc;
+
+    rc = create_thread( &tid, DETACHED, spthread, dev, "spthread" );
+    if (rc)
+    {
+        // "Error in function create_thread(): %s"
+        WRMSG( HHC00102, "E", strerror( rc ) );
+        return 0;
+    }
+    return 1;
+}
+
 // (forward reference)
 static int open_punch( DEVBLK* dev );
 
@@ -55,6 +190,7 @@
 static int cardpch_init_handler( DEVBLK* dev, int argc, char* argv[] )
 {
 int     i;                              /* Array subscript           */
+bool    sockdev = false;
 
     /* Close the existing file, if any */
     if (dev->fd >= 0)
@@ -158,13 +294,52 @@
             continue;
         }
 
+        /* sockdev means the device file is actually
+           a connected socket instead of a disk file.
+           The file name is the socket_spec (host:port)
+           to listen for connections on.
+        */
+        if (strcasecmp( argv[i], "sockdev" ) == 0)
+        {
+            sockdev = true;
+            continue;
+        }
+
         // "%1d:%04X Card: parameter %s in argument %d is invalid"
         WRMSG( HHC01209, "E", LCSS_DEVNUM, argv[i], i+1 );
         return -1;
+
+    } /* end for (i=1; i < argc; i++) Process the driver arguments */
+
+    /* Check for incompatible options... */
+
+    if (sockdev && dev->crlf)
+    {
+        // "%1d:%04X Card: option %s is incompatible"
+        WRMSG( HHC01210, "E", LCSS_DEVNUM,
+            "sockdev/crlf" );
+        return -1;
+    }
+
+    if (sockdev && dev->append)
+    {
+        // "%1d:%04X Card: option %s is incompatible"
+        WRMSG( HHC01210, "E", LCSS_DEVNUM,
+            "sockdev/noclear" );
+        return -1;
+    }
+
+    /* If socket device, create a listening socket
+       to accept connections on.
+    */
+    if (sockdev && !bind_device_ex( dev,
+        dev->filename, onconnect_callback, dev ))
+    {
+        return -1;  // (error msg already issued)
     }
 
     /* Open the device file right away */
-    if (open_punch( dev ) != 0)
+    if (!sockdev && open_punch( dev ) != 0)
         return -1;  // (error msg already issued)
 
     return 0;
@@ -180,9 +355,10 @@
 
     BEGIN_DEVICE_CLASS_QUERY( "PCH", dev, devclass, buflen, buffer );
 
-    snprintf (buffer, buflen, "%s%s%s%s%s IO[%"PRIu64"]",
+    snprintf (buffer, buflen, "%s%s%s%s%s%s IO[%"PRIu64"]",
                 filename,
                 (dev->ascii                ? " ascii"     : " ebcdic"),
+                (dev->bs                   ? " sockdev"   : ""),
                 ((dev->ascii && dev->crlf) ? " crlf"      : ""),
                 (dev->append               ? " append"    : ""),
                 (dev->stopdev              ? " (stopped)" : ""),
@@ -199,6 +375,10 @@
 int             open_flags;             /* File open flags           */
 off_t           filesize = 0;           /* file size for ftruncate   */
 
+    /* Socket punch? */
+    if (dev->bs)
+        return (dev->fd < 0 ? -1 : 0);
+
     open_flags = O_WRONLY | O_CREAT /* | O_SYNC */ |  O_BINARY;
 
     if (!dev->append)
@@ -237,7 +417,18 @@
 {
     /* Close the device file */
     if (dev->fd >= 0)
-        close( dev->fd );
+    {
+        /* Socket punch? */
+        if (dev->bs)
+        {
+            close_socket( dev->fd );
+            // "%1d:%04X Card: client %s, IP %s disconnected from device %s"
+            WRMSG( HHC01211, "I", LCSS_DEVNUM, dev->bs->clientname, dev->bs->clientip, dev->bs->spec );
+        }
+        else
+            close( dev->fd );
+    }
+
     dev->fd = -1;
     dev->stopdev = FALSE;
 
diff -r -a -x .git -x .vs -x 'msvc.AMD64.*' -x 'msvc.dllmod.*' -x 'msvc.debug.*' -x '*.suo' -x '*.ncb' -x '*.user' -x '*.htm' -x WORK -x DICTS -x FILES -x 'allTests.*' -x '*.rej' -x '*.orig' -x AutoBuildCount.h -x '*.cmp*' -x '*.comp*' -Nu hyperion-git/html/hercconf.html hyperion-1/html/hercconf.html
--- hyperion-git/html/hercconf.html	2023-01-29 08:01:10.687591900 -0800
+++ hyperion-1/html/hercconf.html	2023-04-03 13:21:28.008583100 -0700
@@ -2789,6 +2789,20 @@
         option instead.
         <p>
 
+    <dt><code>sockdev</code>
+    <dd><p>
+        Indicates the card punch is a socket device wherein the
+        filename is actually a socket specification instead of a
+        device filename.  When used, there must only be one filename
+        specified in the form: <code>port</code> or <code>host:port</code>.
+        The device then accepts
+        remote connections on the given TCP/IP port,
+        and writes data to the socket instead of to a device
+        file. This allows automatic remote spooling of card punch
+        data. The sockdev option is mutually exclusive with the
+        <code>crlf</code> and <code>append</code> options.
+        <p>
+
     </dl>
     <p>
 
diff -r -a -x .git -x .vs -x 'msvc.AMD64.*' -x 'msvc.dllmod.*' -x 'msvc.debug.*' -x '*.suo' -x '*.ncb' -x '*.user' -x '*.htm' -x WORK -x DICTS -x FILES -x 'allTests.*' -x '*.rej' -x '*.orig' -x AutoBuildCount.h -x '*.cmp*' -x '*.comp*' -Nu hyperion-git/msgenu.h hyperion-1/msgenu.h
--- hyperion-git/msgenu.h	2023-01-29 07:53:19.475214300 -0800
+++ hyperion-1/msgenu.h	2023-04-03 12:55:59.819803700 -0700
@@ -988,7 +988,9 @@
 #define HHC01207 "%1d:%04X Card: file %s: card image exceeds maximum %d bytes"
 #define HHC01208 "%1d:%04X Card: filename is missing"
 #define HHC01209 "%1d:%04X Card: parameter %s in argument %d is invalid"
-//efine HHC01210 - HHC01249 (available)
+#define HHC01210 "%1d:%04X Card: option %s is incompatible"
+#define HHC01211 "%1d:%04X Card: client %s, IP %s disconnected from device %s"
+//efine HHC01212 - HHC01249 (available)
 
 // reserve 01250 - 01299 for Generic device messages
 #define HHC01250 "%1d:%04X %s: error in function %s: %s"
diff -r -a -x .git -x .vs -x 'msvc.AMD64.*' -x 'msvc.dllmod.*' -x 'msvc.debug.*' -x '*.suo' -x '*.ncb' -x '*.user' -x '*.htm' -x WORK -x DICTS -x FILES -x 'allTests.*' -x '*.rej' -x '*.orig' -x AutoBuildCount.h -x '*.cmp*' -x '*.comp*' -Nu hyperion-git/msvc.makefile.includes/MOD_RULES2.msvc hyperion-1/msvc.makefile.includes/MOD_RULES2.msvc
--- hyperion-git/msvc.makefile.includes/MOD_RULES2.msvc	2023-01-18 13:40:14.242356000 -0800
+++ hyperion-1/msvc.makefile.includes/MOD_RULES2.msvc	2023-04-03 13:16:08.359163300 -0700
@@ -98,7 +98,7 @@
     $(linkdll)
     $(MT_DLL_CMD)
 
-$(X)hdt3525.dll:  $(O)cardpch.obj \
+$(X)hdt3525.dll:  $(O)cardpch.obj $(O)sockdev.obj \
                   $(O)hengine.lib $(O)hutil.lib $(O)hsys.lib $(O)hercprod.res
     $(linkdll)
     $(MT_DLL_CMD)
diff -r -a -x .git -x .vs -x 'msvc.AMD64.*' -x 'msvc.dllmod.*' -x 'msvc.debug.*' -x '*.suo' -x '*.ncb' -x '*.user' -x '*.htm' -x WORK -x DICTS -x FILES -x 'allTests.*' -x '*.rej' -x '*.orig' -x AutoBuildCount.h -x '*.cmp*' -x '*.comp*' -Nu hyperion-git/printer.c hyperion-1/printer.c
--- hyperion-git/printer.c	2022-11-26 17:01:06.341640000 -0800
+++ hyperion-1/printer.c	2023-04-03 13:03:21.596979600 -0700
@@ -231,8 +231,7 @@
 {
 int rc;
 
-    /* Write data to the printer file */
-    if (dev->bs)
+    if (dev->bs)    /* socket device? */
     {
         /* Write data to socket, check for error */
         if ((rc = write_socket( dev->fd, buf, len )) < len)
@@ -253,7 +252,7 @@
             return *unitstat = CSW_CE | CSW_DE | CSW_UC;
         }
     }
-    else
+    else /* regular print file */
     {
         /* Write data to the printer file, check for error */
         if ((rc = write( dev->fd, buf, len )) < len)

Fish-Git added a commit that referenced this issue Apr 4, 2023
@Fish-Git
Copy link
Member

Fish-Git commented Apr 4, 2023

Never mind. I used netcat to test it with. Works fine.

Closed by commit 133dafc.

@Fish-Git Fish-Git closed this as completed Apr 4, 2023
@Fish-Git Fish-Git removed the IN PROGRESS... I'm working on it! (Or someone else is!) label Apr 4, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Enhancement This issue does not describe a problem but rather describes a suggested change or improvement.
Projects
None yet
Development

No branches or pull requests

3 participants