[Search for users] [Overall Top Noters] [List of all Conferences] [Download this site]

Conference jamin::pathworks32

Title:Digital PATHWORKS 32
Moderator:SPELNK::curless
Created:Fri Nov 01 1996
Last Modified:Fri Jun 06 1997
Last Successful Update:Fri Jun 06 1997
Number of topics:337
Total number of notes:1612

286.0. "On Windows NT, SPAWN32 doesn't work, but the V6 version of SPAWNER.EXE does!" by SUFRNG::WSA028::DOWNING_L () Thu May 08 1997 20:20

What am I doing wrong?


I have a customer who is not able to invoke a batch file on her PATHWORKS32
client using SPAWN32.EXE.

It works for her if she uses SPAWN.EXE from the PATHWORKS for DOS and Windows
v6.0 kit.

I'm able to fully recreate this problem on my own PC.

Perhaps I'm missing something, and hope someone here can help.


On the PC:
NCP LIST KNOWN OBJECTS

Taskname        #       Use     File            "Arguments"

PCDAILY         0       1       D:\WINNT40\SYSTEM32\PCDAILY.BAT
FAL             17      1       D:\PW32\FAL32.EXE

(By the way, FAL works.  $DIR pcnode::  gives me a directory listing.)


The D:\WINNT40\SYSTEM32\PCDAILY.BAT file consists of this:

        echo on
        copy c:\sample m:

For the sake of expediency, I set DEFAULT incoming access to ALL.

I added an account called DEFAULT and gave it priv's to 'Log on as a Batch Job'.

Both nodes are defined in each other's NCP databases.

Control Panel, Services shows DEC PATHWORKS32 Spawner Status=Started and
Startup=Automatic.

PATHWORKS Event Logger has this message from when I last rebooted:

i  18:40:02 SPAW0001S: spawner (spawner) Version 1,0,0,2 Loaded

I've mapped a drive (\M:) to a PATHWORKS for OpenVMS service where I have RW
priv's.

On the VMS system:
$TYPE MORGAN::"TASK=PCDAILY"

My PATHWORKS Event Logger immediately gets this new message (wrapped to 2 lines)

i 18:55:00  SPAW9999N: Command D:\WINNT40\SYSTEM32\PCDAILY.BAT -use 2
-access 3 executed!

 An aside: I see by page 17-4 of the PATHWORKS 32 User's Guide, that
           the Job Spawner has 'Arguments', which I'm afraid I don't
           comprehend.  It looks like "-access 3" refers to Argument
           #4, where 3=All Access.  I say this, because before I defined
           DEFAULT access to ALL, "-access" came back as 0, which means
           No Access.

The message I get back on the VMS side is:
%TYPE-W-OPENIN, error opening ORYGUN::"TASK=PCDAILY" as input
-RMS-E-ACC, ACP file access failed
-SYSTEM-F-CONNECFAIL, connect to network object timed-out or failed

No errors appear in the standard WNT Event Viewer.

NCP SHOW KNOWN LINKS on the PC shows

State   Skt     Node    Local   Remote  Local           Remote
                        Addr    Addr    #   Name        #  Name

Idle    1       0.0     62977   0       0   PCDAILY     0

Do I need to fiddle with the 'Job Spawner Calls?'

-Linda
T.RTitleUserPersonal
Name
DateLines
286.1SUFRNG::WSA028::DOWNING_LThu May 08 1997 21:022
I mean, do I need to fiddle with 'arguments' in the BAT file? 
If so, what would that look like?
286.2you're right, it fails...JAMIN::RUZICHPATHWORKS Client EngineeringFri May 09 1997 12:189
.0> I have a customer who is not able to invoke a batch file on her PATHWORKS32
.0> client using SPAWN32.EXE.

I can't make it work either, and I have responsibility for the spawner.
Executable objects work OK, but not batch jobs.

I'm investigating, I'll let you know.

-Steve
286.3How about...SPELNK::curlessFri May 09 1997 14:035
How about asking the system to launch it instead... i.e. call command.com, or
cmd.exe (for NT) and provide the batch file as an argument?

Jeff
286.4how?SUFRNG::WSA028::DOWNING_LFri May 09 1997 20:2221

First, I run this command on the PC. It runs the batch file successfully:
D:\WINNT40\SYSTEM32> CMD /C PCDAILY

Now, am I supposed to create an NCP object for CMD?

ncp define object cmd number 0 file "d:\winnt40\system32\cmd.exe /c pcdaily.bat"

$ TYPE MORGAN::"TASK=CMD"
%TYPE-W-OPENIN, error opening MORGAN::"TASK=CMD" as input
-RMS-E-ACC, ACP file access failed
-SYSTEM-F-CONNECFAIL, connect to network object timed-out or failed

$type morgan:"task=cmd /c pcdaily"
%TYPE-W-OPENIN, error opening morgan::"TASK=CMD.EXE -C PCDAILY" as input
-RMS-F-DEV, error in device name or inappropriate device type for operation

Please give me an example, because my S.W.A.G.'s ain't gettin it.

-Linda
286.5SPELNK::curlessMon May 12 1997 10:5712
Well, I just set up what you are attempting and it almost works.  The fact
that you are getting an access problem means you need to provide a username
and password for the inbound connection.  Look up your access.

Once that is solved, type node"user password"::"task=cmd" works, well... the
command is launched, but ... the batch file does not seem to execute.

remember, there is no path for the application/batch file so ALL paths must
be fully qualified.

Jeff
286.6Here is how to make spawner workSPELNK::curlessMon May 12 1997 14:2722
Well, SPAWN32 will not spawn batch files.  However, I just wrote a progam
that can be spawned, and IT will start the batch file:

ftp from sack.lkg.dec.com, launch.exe
then from NCP:

define object test number 0 file "c:\launch.exe c:\pcdaily.bat"

and  add access control, launch will start pcdaily.bat.

Remember, all paths MUST be fully qualified.

Jeff

P.S.

type node"user password"::"task=test" will result in the batch file running,
but will also result in a -system-f-reject on the OpenVMS side, 'cause no one
uses the socket....  working on it.

Jeff
286.7And the program... not finished yetSPELNK::curlessMon May 12 1997 14:29247

///////////////////////////////////////////////////////////////////////////////
//
//    Copyright (c) 1997
//    by DIGITAL Equipment Corporation, Maynard, Mass.
//    All Rights Reserved.
//
//    This software is furnished under a license and may be used and  copied
//    only  in  accordance  with  the  terms  of  such  license and with the
//    inclusion of the above copyright notice.  This software or  any  other
//    copies  thereof may not be provided or otherwise made available to any
//    other person.  No title to and ownership of  the  software  is  hereby
//    transferred.
//
//    The information in this software is subject to change  without  notice
//    and  should  not  be  construed  as  a commitment by DIGITAL Equipment
//    Corporation.
//
//    DIGITAL assumes no responsibility for the use or  reliability  of  its
//    software on equipment which is not supplied by DIGITAL.
//
//    Author:	Jeff Curless
//    File:	    launch.cpp
//    Date:	    9705.12
//
///////////////////////////////////////////////////////////////////////////////
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <prgpre.h>
#include <process.h>

#pragma comment(lib,"pwsock32.lib")
#pragma comment(lib,"nmapi32.lib")

/////////////////////////////////////////////////////////////////////////////
//
//  Gobals
//
/////////////////////////////////////////////////////////////////////////////

unsigned char *m_wsaData=NULL;
WORD     m_wVersionRequested;

/////////////////////////////////////////////////////////////////////////////
//
//  Initialize
//
/////////////////////////////////////////////////////////////////////////////

BOOL
Initialize( void )
{
    return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
//
//  RemoveArgument
//
/////////////////////////////////////////////////////////////////////////////

void
RemoveArgument( int *argc, char *argv[], int Index )
{
    int i,j;

    j = Index;
    i = Index+1;
    for( ; i < *argc ; i++,j++){
        argv[j] = argv[i];
        }

    argv[*argc] = NULL;
    *argc = *argc - 1;
}

/////////////////////////////////////////////////////////////////////////////
//
//  GetSocketNumber
//
/////////////////////////////////////////////////////////////////////////////

BOOL
GetSocketNumber(int *argc, char *argv[], short *Socket )
{
    int i;

    for( i = 0 ; i < *argc ; i++ ){
        if( !strcmpi(argv[i],"-use") ){
            *Socket = atoi(argv[i+1]);
            RemoveArgument( argc, argv, i );
            RemoveArgument( argc, argv, i );
            return TRUE;
            }
        }

    return FALSE;
}

/////////////////////////////////////////////////////////////////////////////
//
//  GetAccessCode
//
/////////////////////////////////////////////////////////////////////////////

BOOL
GetAccessCode(int *argc, char *argv[], int *Access )
{
    int i;

    for( i = 0 ; i < *argc ; i++ ){
        if( !strcmpi(argv[i],"-access") ){
            *Access = atoi( argv[i+1] );
            RemoveArgument( argc, argv, i );
            RemoveArgument( argc, argv, i );
            return TRUE;
            }
        }

    return FALSE;
}

/////////////////////////////////////////////////////////////////////////////
//
//  Shutdown
//
/////////////////////////////////////////////////////////////////////////////

BOOL
Shutdown( short Socket )
{
    short errval;

    if( Socket != -1 ){
        SktSClose(Socket,&errval);
        }
    return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
//
//  Accept
//
/////////////////////////////////////////////////////////////////////////////

BOOL
Accept( short *Socket )
{
    short  errval;
    struct sockaddr_dn src;
    int    sourcelen;
    short  NewSocket;


    NewSocket = SktAccept(*Socket,&src,&sourcelen,&errval);
    if( NewSocket != -1 ){
        Shutdown( *Socket );
        *Socket = NewSocket;
        }
    return TRUE;
}


/////////////////////////////////////////////////////////////////////////////
//
//  BuildCommand
//
/////////////////////////////////////////////////////////////////////////////

void
BuildCommand( char *Command, int argc, char *argv[] )
{
    int i;

    Command[0] = '\0';
    for( i = 1 ; i <= argc ; i++ ){
        if( argv[i] != NULL ){
            strcat( Command, argv[i] );
            strcat( Command, " " );
            }
        }
}

/////////////////////////////////////////////////////////////////////////////
//
//  Main
//
/////////////////////////////////////////////////////////////////////////////

int
main( int argc, char *argv[] )
{
    BOOL    bResult;
    int     iResult;
    short   Socket=-1;
    int     Access;
    char    Command[1024];
    
    //
    // Initialize
    //
    bResult = Initialize();
    if(bResult != TRUE ){
        return 32;
        }

    //
    // Obtain and remove socket number from the arguments
    //
    bResult = GetSocketNumber( &argc, argv, &Socket );
    if( bResult != TRUE ){
        return 33;
        }

    //
    // Obtain and remove the access code from the arguments
    //
    bResult = GetAccessCode( &argc, argv, &Access );
    if( bResult != TRUE ){
        return 34;
        }

    bResult = Accept( &Socket );
    if( bResult != TRUE ){
        return 36;
        }

    //
    // Launch the requested program
    //
    BuildCommand( Command, argc, argv );
    iResult = WinExec( Command, SW_HIDE );

    //
    // shutdown the socket, and winsock
    //
    bResult = Shutdown(Socket);
    if( bResult != TRUE ){
        return 35;
        }

    return iResult;
}

286.8looks like you're getting it!TOHOPE::WSA028::DOWNING_LMon May 12 1997 15:0014
Thanks for jumping on this one.
re: .5  I have set DEFAULT access to ALL.  Since SPAWNER.EXE works, I assume
        that security is not causing this problem.

re: .6 By giving the ftp address for launch.exe, am I allowed to go get it?
       Anonymous access does not let me in.

re: .7  Should I compile and build the example in .7?  
     but the title of the note says it is unfinished.


I DO have an IPMT case elevated for this problem.  The case number is CFS.51099.

286.9SPELNK::curlessMon May 12 1997 16:119
ftp access is allowed by anonymous but sometimes it does NOT like
the password, so access with NO password.

.7 is the code, it is incomplete in that it currently does not handle
the inbound socket connection properly... this results in the error message,
and no data transfer, but... the batch file runs.

Jeff
286.10SPELNK::curlessMon May 12 1997 16:1413
Could someone tell me:

1) When a batch file is launched on an NT v3.51 system, does the output
   of that batch file get sent over to the caller?  i.e. type node::"task=test"
   results in either a dump of data from the remote system, or... nothing,
   which is it?

2) With v6.0, what happens?

I dont't want to reconfigure.

Jeff
286.11Launch no longer reports errorsSPELNK::curlessMon May 12 1997 17:367
never mind... just did it, nothing is sent... so launch.exe has been
updated, no errors reported, non-debug version. 

Have fun,

Jeff
286.12followupTOHOPE::WSA028::DOWNING_LThu May 15 1997 14:158
Thanks. LAUNCH.EXE does work nicely.

Is it a long term solution?

Will it become part of the PATHWORKS product set?
Will SPAWN32.EXE be fixed?

-Linda
286.13works for me, but not for the customerVMSNET::BAUERWed May 21 1997 13:1212
    
    Well, it worked nicely for me, but does nothing at the customers
    system.  They get the same success message in their PATHWORKS event
    viewer that I do, but the batch job does not run.
    
    We verified that SPAWN32 will invoke an EXE file on her computer by
    creating a DECnet object for CLOCK.EXE, and that works just fine.
    
    Is there a debug version of LAUNCH.EXE that we can try?
    Will SPAWN32.EXE be fixed?
    
    -Linda
286.14and, will LAUNCH pass arguments?VMSNET::BAUERWed May 21 1997 13:2217
    During our tests, we put both pcdaily.bat and launch.exe in the root of
    C:. PCDAILY.BAT simply copies files from one directory to another, and
    this is how we defined the object:
    ncp define object pcdaily number 0 file "c:\launch.exe c:\pcdaily.bat"
    
    
    Normally, her DECnet object points to a batch file and
    is passed the DECnet nodename of the client as an argument.
    
    ncp define object pcdaily number 0 file "c:\pcdaily.bat pc33"
    
    Will LAUNCH.EXE allow that?
    Will this work?
    ncp define object pcdaily number 0 file "c:\launch.exe c:\pcdaily.bat
    pc33"
    
    
286.15SPELNK::curlessWed May 21 1997 16:078
It should, no problem.  remember that the pcdaily.bat file MUST CONTAIN
absolute paths for everything.  Do no rely on the path or any environment
variables being present.

As for a debug version... take the code.

Jeff
286.16what does 'take the code' mean?QBUS::L_BAUERWed May 21 1997 20:346
    
    Again, thanks for taking the time to answer this note.
    
    But I'm sorry, I don't understand what you mean by 'take the code'.
    
    
286.17no path or environment variablesVMSNET::BAUERWed May 21 1997 22:103
    ...and, the .BAT file is not relying on any path or environment
    variables. It just doesn't work if SPAWN32 tries to kick it off.
    It does work with SPAWNER.EXE
286.18This codeSPELNK::curlessThu May 22 1997 19:00309
Take this code... this is an updated version of the earlier code.

Jeff


///////////////////////////////////////////////////////////////////////////////
//
//    Copyright (c) 1997
//    by DIGITAL Equipment Corporation, Maynard, Mass.
//    All Rights Reserved.
//
//    This software is furnished under a license and may be used and  copied
//    only  in  accordance  with  the  terms  of  such  license and with the
//    inclusion of the above copyright notice.  This software or  any  other
//    copies  thereof may not be provided or otherwise made available to any
//    other person.  No title to and ownership of  the  software  is  hereby
//    transferred.
//
//    The information in this software is subject to change  without  notice
//    and  should  not  be  construed  as  a commitment by DIGITAL Equipment
//    Corporation.
//
//    DIGITAL assumes no responsibility for the use or  reliability  of  its
//    software on equipment which is not supplied by DIGITAL.
//
//    Author:	Jeff Curless
//    File:	    launch.cpp
//    Date:	    9705.12
//
///////////////////////////////////////////////////////////////////////////////
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <prgpre.h>
#include <process.h>

#pragma comment(lib,"pwsock32.lib")
#pragma comment(lib,"nmapi32.lib")

/////////////////////////////////////////////////////////////////////////////
//
//  RemoveArgument
//
//  Parameters
//      argc        - pointer to the number of arguments that follow
//      argv        - the argv array structure
//      Index       - the index to remove from the argv array
//
//  Returns
//      Nothing
// 
//  Remarks
//      This routine removes the index item from the argv array, and updates
//      the argc counter.  The last element in the array will will be set
//      to NULL.
//
/////////////////////////////////////////////////////////////////////////////

void
RemoveArgument( int *argc, char *argv[], int Index )
{
    int i,j;

    j = Index;
    i = Index+1;
    for( ; i < *argc ; i++,j++){
        argv[j] = argv[i];
        }

    *argc = *argc - 1;
    argv[*argc] = NULL;
}

/////////////////////////////////////////////////////////////////////////////
//
//  GetSocketNumber
//
//  Obtain and remove the socket number from the command line arguments
//
//  Parameters
//      argc        - pointer to the number of arguments that follow
//      argv        - the argv array structure
//      Socket      - pointer to the location used for storing the socket
//
//  Returns:
//      TRUE        - if socket number was found
//      FALSE       - if not.
//
//  Remarks:
//      This routine looks for the socket number passed... the command
//      line was STUPIDLY setup to be -use #, where the number is separated
//      by white space from the -use.  This means none of our standard
//      switch parsing can be used.  
//
//      If the -use switch is found, it and the assumed to be following
//      socket number are removed from the command line arguments
//
/////////////////////////////////////////////////////////////////////////////

BOOL
GetSocketNumber(int *argc, char *argv[], short *Socket )
{
    int i;

    for( i = 0 ; i < *argc ; i++ ){
        if( !strcmpi(argv[i],"-use") ){
            *Socket = atoi(argv[i+1]);
            RemoveArgument( argc, argv, i );
            RemoveArgument( argc, argv, i );
            return TRUE;
            }
        }

    return FALSE;
}

/////////////////////////////////////////////////////////////////////////////
//
//  GetAccessCode
//
//  Obtain and remove the socket number from the command line arguments
//
//  Parameters
//      argc        - pointer to the number of arguments that follow
//      argv        - the argv array structure
//      Access      - pointer to the location used for storing the access
//
//  Returns:
//      TRUE        - if access flag was found
//      FALSE       - if not.
//
//  Remarks:
//      This routine looks for the access code passed... the command
//      line was STUPIDLY setup to be -access #, where the number is separated
//      by white space from the -access.  This means none of our standard
//      switch parsing can be used.  
//
//      If the -access switch is found, it and the assumed to be following
//      access code are removed from the command line arguments
//
/////////////////////////////////////////////////////////////////////////////

BOOL
GetAccessCode(int *argc, char *argv[], int *Access )
{
    int i;

    for( i = 0 ; i < *argc ; i++ ){
        if( !strcmpi(argv[i],"-access") ){
            *Access = atoi( argv[i+1] );
            RemoveArgument( argc, argv, i );
            RemoveArgument( argc, argv, i );
            return TRUE;
            }
        }

    return FALSE;
}

/////////////////////////////////////////////////////////////////////////////
//
//  Shutdown
//
//  shutdown the connection with the remote machine
//
//  Parameters
//      Socket      - The socket number passed in from the initial connection
//
//  Returns:
//      TRUE    - if the connection was shutdown
//      FALSE   - if not
//
//  Remarks:
//      Toss error codes 'cause it really doesn't matter if we can't close
//      the socket... the other end will complain eventually.
//
/////////////////////////////////////////////////////////////////////////////

BOOL
Shutdown( short Socket )
{
    short errval;

    if( Socket != -1 ){
        if( !SktSClose(Socket,&errval) ){
            return TRUE;
            }
        }
    return FALSE;
}

/////////////////////////////////////////////////////////////////////////////
//
//  AcceptConnect
//
//  Mark the inbound socket as accepting connections...
//
//  Parameters
//      Socket      - the socket we are to mark
//
//  Returns:
//      TRUE        - if socket can accept connections
//      FALSE       - if not.
//
//  Remarks:
//      Ignore the actual error code, because the remote end will report the
//      error to the user.
//
/////////////////////////////////////////////////////////////////////////////

BOOL
AcceptConnect( short Socket )
{
    short  errval;

    if( !SktSetSockOpt(Socket,DNPROTO_NSP,DSO_CONACCEPT,NULL,0,&errval) ){
        return TRUE;
        }
    return TRUE;
}


/////////////////////////////////////////////////////////////////////////////
//
//  BuildCommand
//
//  Construct a command line
//
//  Parameters
//      Command     - pointer to a large command line
//      argc        - number of arguments
//      argv        - the arguments
//
//  Returns:
//      nothing.
//
//  Remarks:
//      No error checking is done.
//
/////////////////////////////////////////////////////////////////////////////

void
BuildCommand( char *Command, int argc, char *argv[] )
{
    int i;

    Command[0] = '\0';
    for( i = 1 ; i <= argc ; i++ ){
        if( argv[i] != NULL ){
            strcat( Command, argv[i] );
            strcat( Command, " " );
            }
        }
}

/////////////////////////////////////////////////////////////////////////////
//
//  Main
//
/////////////////////////////////////////////////////////////////////////////

int
main( int argc, char *argv[] )
{
    BOOL    bResult;
    int     iResult;
    short   Socket=-1;
    int     Access;
    char    Command[1024];
    
    //
    // Obtain and remove socket number from the arguments
    //
    bResult = GetSocketNumber( &argc, argv, &Socket );
    if( bResult != TRUE ){
        return 1;
        }

    //
    // Obtain and remove the access code from the arguments
    //
    bResult = GetAccessCode( &argc, argv, &Access );
    if( bResult != TRUE ){
        return 2;
        }

    bResult = AcceptConnect( Socket );
    if( bResult != TRUE ){
        return 3;
        }

    //
    // Launch the requested program
    //
    BuildCommand( Command, argc, argv );
    iResult = WinExec( Command, SW_HIDE );

    //
    // shutdown the socket, and winsock
    //
    bResult = Shutdown(Socket);
    if( bResult != TRUE ){
        return 4;
        }

    return iResult;
}

286.19prototype DECnet object exampleJAMIN::RUZICHPATHWORKS Client EngineeringFri May 23 1997 13:32262
This is not directly relevant to the topic of spawner problems with batch
files, but it seemed close enough that someone may find this useful.

What follows is a prototype DECnet object program. There's a VMS command
file embedded as a comment, for driving the object program. 

-Steve Ruzich

/* protobj.c - prototype PATHWORKS 32 V7.0 DECnet Object
 *
 * Copyright (C) 1997 by
 * Digital Equipment Corporation, Maynard, Mass.
 *
 * This software is furnished under a license and may be used and copied
 * only  in  accordance  with  the  terms  of such  license and with the
 * inclusion of the above copyright notice. This software or  any  other
 * copies thereof may not be provided or otherwise made available to any
 * other person. No title to and ownership of  the  software  is  hereby
 * transferred.
 *
 * The information in this software is subject to change without  notice
 * and  should  not be  construed  as  a commitment by Digital Equipment
 * Corporation.
 *
 * Digital assumes no responsibility for the use or  reliability  of its
 * software on equipment which is not supplied by Digital.
 *
 * Steve Ruzich, 1-May-1997
 *
 * revision:
 *
 *	15-May-1997	Add the "disconnect" function, to demonstrate that
 *				the connection can be closed on either the 
 *				client or the server side.
 *
 *	20-May-1997	Add the VMS command file example as a comment.
 */

/*
 * Here's a VMS command file to drive the test:

$ ! 
$ ! PROTOBJ.COM - VMS command file to invoke DECnet object protobj on PW32.
$ ! This object will just echo back commands on the same socket. If we write
$ ! a message of zero length, it will respond with a special message.
$ ! 
$ ! Replace the 'xxxxx' string with the name of a PW32 node.  Edit the 'name'
$ ! and 'account' fields as you need.
$ !
$ ! This command file will invoke the object twice:
$ ! The first time, write then read two messages and then close the socket.
$ ! This will cause the disconnect to be issued on this side (client side).
$ ! The second time, write & read one message, then send a "disconnect"
$ ! request. The object will then disconnect the socket.
$ ! 
$ set noveri
$ open/write xx: xxxxx"name password"::"0=protobj"
$ write sys$output "Socket is open, write and read a couple messages..."
$ write xx: "This is a data record"
$ read xx: rec
$ sho sym rec
$ write xx: ""
$ read xx: rec
$ sho sym rec
$ close xx:
$ write sys$output "All done."
$ !
$ open/write xx: xxxxx"name password"::"0=protobj"
$ write sys$output "Socket is open, write and read one message, then tell the object to disconnect"
$ write xx: "This is a data record"
$ read xx: rec
$ sho sym rec
$ write xx: "disconnect"
$ close xx:
$ write sys$output "All done."
$ ! 
$ ! end of command file PROTOBJ.COM
$ !

 *
 */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <socket.h>
#include <dn.h>
#include <wtypes.h>	// TRUE, FALSE, BOOLEAN

#define BUF_SIZ 512
//#define DEBUG1 1	// Temp hack debug printf

word sock;			// socket number
int acc_code;		// access code: 0 - none, 1 - read only, 
					// 2 - write only, 3 - all
BOOLEAN debug_mode = FALSE;	// '-debug' specified on command line, T/F


//
// obj_init - one-time setup for socket, and any other housekeeping
// 
// return:	TRUE - initialization succeeded
//			FALSE - grave error found
//
BOOLEAN obj_init() {

	// ensure DECnet is installed
	if (dnet_installed(0X6E, "DNP") == -1) {
		printf("Decnet is not installed\n");
		return(FALSE);
	}

	if( setsockopt(sock, DNPROTO_NSP, DSO_CONACCEPT, 0, 0) < 0 ) {
		// whatever error processing you like
		perror("setsockopt(DSO_CONACCEPT) error\n");
		return(FALSE);
	}
	return(TRUE);
}

//
// obj_loop - recv/process/send
//
// Just echo the incoming data back on the same socket.
// Return on EOF (then the calling routine closes the socket).
//
// This means that the client usually does the disconnect.
// Let's introduce a variation: if the data says "disconnect",
// then we do the disconnect first. That should all work OK.
//
// This code does not make a special case of out-of-band messages.
//
void obj_loop() {
	int stat = 1;
	int count;
	char data[BUF_SIZ];
	char null_msg[] = "null buffer received";

	while (stat>=0) {
		//
		// Receive the incoming message, check the count.
		//
		count = recv(sock, data, BUF_SIZ, 0);
		if (count<0) {
			perror("recv error");
			return;
		}
		if (debug_mode) {
			data[count] = 0;		// zero-terminate
			printf("%d bytes read - '%s'\n", count, data);
		}
		if (count>0) {
			//
			// Non-zero count, so the incoming message contains
			// some data. If message says "disconnect", 
			// then return (and the main routine will close
			// the socket).  Otherwise, just echo the data back
			// to the client.
			//
			if( strcmp(data,"disconnect") == 0 ) {
				if (debug_mode) {
					printf("client tells us to disconnect\n");
				}
				return;
			}
			stat = send(sock, data, count, 0);
			if (debug_mode) {
				printf("send(%d,'%s',%d) returns %d\n",	
					sock, data, count, stat);
			}
			if (stat == -1)
				perror("send");
		}
		else {
			//
			// Count is zero - maybe this is a zero-length message, 
			// or maybe the connection has been terminated.
			// routine dnet_eof() will tell us ...
			// 0 = link is OK, 1 = terminated.
			// If there is a null incoming message, send back a 
			// response saying "null message received".
			//
			stat = dnet_eof(sock);
			if (debug_mode) {
				printf("dnet_eof returns %d\n",stat);
			}
			if (stat == 1)
				return;
			stat = send(sock, null_msg, sizeof(null_msg), 0);
			if (debug_mode) {
				printf("send(%d,'%s',%d) returns %d\n",	
					sock, null_msg, sizeof(null_msg), stat);
			}
			if (stat == -1)
				perror("send");
		}
	}
}
//
// main entry point - protobj.c - prototype DECnet object program
//
void __cdecl
main(argc, argv)
int argc;
char *argv[]; {

#ifdef DEBUG1
	int i;
	// Debug
	for (i=0;i<argc;i++) {
		printf("  argv[%d]= '%s'\n",i,argv[i]);
	}
#endif

	//
	// We must recognize '-use N' on the command line,
	// where 'N' is the integer socket number.
	// The '-access N' will be present, but it is up
	// to the application designer to decide what 
	// meaning the codes (read only, etc.) have to
	// the particular application. We ignore it here.
	// The '-debug' means that the spawner has been
	// started 'spawn32 -debug' in a DOS box, and that
	// it will spawn each DECnet object in a DOS box.
	// Thus, '-debug' means that a printf() in the 
	// DECnet object will write something where you can
	// see it, to help in debugging. (Normally, spawn32
	// will execute the DECnet objects as detached
	// executables, so you don't even know if they are
	// running.
	//

	for( ; argc > 0 ; argc--, argv++ ) {
		if( strcmp(*argv, "-use") == 0 ) {
			sock = atoi(*++argv);
			argc--;
		}
		else if( strcmp(*argv, "-access") == 0 ) {
			acc_code = atoi(*++argv);
			argc--;
		}
		else if( strcmp(*argv, "-debug") == 0 ) {
			debug_mode = TRUE;
		}
	}

	// Initialize, then receive data, process, send response, until done

	if (obj_init())
		obj_loop();
	if (sclose(sock) == -1) {
		perror("sclose error");
	}
	if (debug_mode) {
		// Assume we're running in console mode.  Avoid immediate
		// exit, which would discard the DOS box before we can
		// read any messages.
		printf("hit return to exit:\n");
		getchar();
	}
	exit(0);		// Normal exit
}