From:	CSBVAX::CSBVAX::MRGATE::"SMTP::CRVAX.SRI.COM::RELAY-INFO-VAX"  3-FEB-1989 01:44
To:	MRGATE::"ARISIA::EVERHART"
Subj:	VMS subprocess command execution 2/2


Received: From KL.SRI.COM by CRVAX.SRI.COM with TCP; Thu,  2 FEB 89 22:01:11 PDT
Received: from B.PSC.EDU (cpwscb.psc.edu) by KL.SRI.COM with TCP; Thu, 2 Feb 89 21:55:44 PST
Date:    Fri, 3 Feb 1989 0:57:43 EST
From:    GENTZEL @ B.PSC.EDU (Dave Gentzel)
Message-Id: <890203005743.21001b51@B.PSC.EDU>
Subject: VMS subprocess command execution 2/2
To:      INFO-VAX @ KL.SRI.COM
X-Vmsmail-To: SMTP%"INFO-VAX@KL.SRI.COM"

+-+-+-+ Beginning of part 2 +-+-+-+
X`009status = SYS$GETDVIW(
X`009    0,`009`009`009/* EFN */
X`009    *channel,`009`009/* Channel */
X`009    0,`009`009`009/* Device name */
X`009    &getdvi_itemlist,`009/* Item list */
X`009    &iosb,`009`009/* I/O status block */
X`009    0, 0,`009`009/* No AST address or parameter */
X`009    0);`009`009`009/* Null argument */
X    /* If the GETDVI worked then copy the answer into a Descriptor */
X    if (status & 1)
X`009status = iosb.status;
X    if (status & 1)
X`009status = STR$COPY_R(name, &name_length, &name_string);
X    return status;
X`125
X
X/* Create a subprocess.  It will run LOGINOUT.EXE to create a CLI for the
X   subprocess.  Input and output devices will be the mailbox names defined in
X   the DCL structure.  Returns condition code. */
Xstatic unsigned int
X#ifdef __STDC__
Xcreate_process(struct process *process_info)
X#else
Xcreate_process(process_info)
Xstruct process *process_info;
X#endif
X`123
X    static $DESCRIPTOR(image_name, "SYS$SYSTEM:LOGINOUT.EXE");
X    static $DESCRIPTOR(process_name, "MAKE_PROC_!UL");
X    struct dsc$descriptor final_process_name = DYNAMIC_DESCRIPTOR;
X    unsigned int sequence_number = 0;
X#ifdef USE_CREPRC
X    int code = JPI$_PRIB;
X    unsigned int prio;
X#else
X    unsigned int flags = CLI$M_NOWAIT `124 CLI$M_NOKEYPAD;
X#endif
X    unsigned int status;
X
X#ifdef USE_CREPRC
X    status = LIB$GETJPI(&code, 0, 0, &prio, 0, 0);
X    if (!(status & 1))
X`009return status;
X#endif
X
V    /* Now Loop until either the process name is unique or an error other tha
Xn
X       SS$_DUPLNAM occurs. */
X    do
X    `123
X`009/* Use sequence number with the base name to get a final process
X`009   name. */
X`009LIB$SYS_FAO(&process_name, 0, &final_process_name, sequence_number++);
X`009/* Now try to create the process */
X#ifdef USE_CREPRC
X`009status = SYS$CREPRC(
X`009    &process_info->pid,`009`009/* Process ID */
X`009    &image_name,`009`009/* Image to run */
X`009    &process_info->input_name,`009/* Definition for SYS$INPUT */
X`009    OUTPUT_NAME,`009`009/* Definition for SYS$OUTPUT */
X`009    OUTPUT_NAME,`009`009/* Definition for SYS$ERROR */
X`009    0,`009`009`009`009/* Privileges (default to parent's) */
X`009    0,`009`009`009`009/* Quotas (default to parent's) */
X`009    &final_process_name,`009/* Name of the process */
X`009    prio,`009`009`009/* Base priority (use parent's) */
X`009    0,`009`009`009`009/* UIC (default to parent's) */
X`009    process_info->term_unit,`009/* Termination mailbox unit number */
X`009    0);`009`009`009`009/* Status flags */
X#else
X`009status = LIB$SPAWN(
X`009    0,
X`009    &process_info->input_name,
X`009    OUTPUT_NAME,
X`009    &flags,
X`009    &final_process_name,
X`009    &process_info->pid,
X`009    0,
X`009    0,
X`009    term_ast,
X`009    process_info,
X`009    0,
X`009    0);
X#endif
X`009/* Free up the dynamic string and see if we have to loop again */
X`009STR$FREE1_DX(&final_process_name);
X    `125 while (status == SS$_DUPLNAM);
X
X    return status;
X`125
X
Xstatic unsigned int
X#ifdef __STDC__
Xwrite_setup_commands(struct process *process_info)
X#else
Xwrite_setup_commands(process_info)
Xstruct process *process_info;
X#endif
X`123
X    static $DESCRIPTOR(set_noon_command, "SET NOON!'F$VERIFY(0)");
X    unsigned int status;
X
V    /* Issue a command to not kill the process on error and turn off verify *
X/
X    status = write_command(process_info, &set_noon_command);
X    if (status & 1)
X    `123
X`009static $DESCRIPTOR(open_prefix, "OPEN/WRITE MAKE_STATUS_MBX ");
X`009struct dsc$descriptor open_command = DYNAMIC_DESCRIPTOR;
X
X`009status = STR$CONCAT(&open_command,
X`009`009`009    &open_prefix, &process_info->status_name);
X`009if (status & 1)
X`009    status = write_command(process_info, &open_command);
X`009STR$FREE1_DX(&open_command);
X    `125
X    return status;
X`125
X
X/* Write a DCL command to the subprocess's input device.  Returns condition
X   code. */
Xstatic unsigned int
X#ifdef __STDC__
Xwrite_command(struct process *process_info,
X`009      struct dsc$descriptor *command_line)
X#else
Xwrite_command(process_info, command_line)
Xstruct process *process_info;
Xstruct dsc$descriptor *command_line;
X#endif
X`123
X    /* Write the command out to the subprocesses input channel */
X    return SYS$QIO(
X`0090,`009`009`009`009/* No event flag */
X`009process_info->input_channel,`009/* IO Channel */
X`009IO$_WRITEVBLK `124 IO$M_NOW,`009/* IO operation - WRITE */
X`009&process_info->input_iosb,`009/* IO status block */
X`0090,`009`009`009`009/* No AST routine */
X`0090,`009`009`009`009/* No AST argument */
X`009command_line->dsc$a_pointer,`009/* Address of output buffer (P1) */
X`009command_line->dsc$w_length,`009/* Length of output buffer (P2) */
X`0090, 0, 0, 0);`009`009`009/* No more arguments (P3-P6) */
X`125
X
X#ifdef FANCY_OUTPUT
X/* Read in the output that the subprocess got back from the command that it
X   issued.  The routine will keep reading until there is no more output to be
X   read.  Return condition code. */
Xstatic unsigned int
X#ifdef __STDC__
Xpost_output_read(struct process *process_info)
X#else
Xpost_output_read(process_info)
Xstruct process *process_info;
X#endif
X`123
X    /* Read in the output that the suprocess has written to the mailbox */
X    return SYS$QIO(
X`0090,`009`009`009`009/* No event flag */
X`009process_info->output_channel,`009/* IO channel */
X`009IO$_READVBLK,`009`009`009/* IO function - READ */
X`009&process_info->output_iosb,`009/* IO status block */
X`009read_ast,`009`009`009/* AST address */
X`009process_info,`009`009`009/* AST argument */
X`009process_info->output_buffer,`009/* Buffer address (P1) */
X`009sizeof process_info->output_buffer,/* Buffer length (P2) */
X`0090, 0, 0, 0);`009`009`009/* No more arguments (P3-P6) */
X`125
X#endif
X
X#ifdef USE_CREPRC
X/* Post an asyncronous I/O to read information off the termination channel.
X   When the subprocess is terminated a final message will be written to that
X   channel.  Returns condition code. */
Xstatic unsigned int
X#ifdef __STDC__
Xpost_termination_read(struct process *process_info)
X#else
Xpost_termination_read(process_info)
Xstruct process *process_info;
X#endif
X`123
X    /* Post an async IO on the termination channel */
X    return SYS$QIO(
X`0090,`009`009`009`009/* No event flag */
X`009process_info->term_channel,`009/* IO channel */
X`009IO$_READVBLK,`009`009`009/* IO function - READ */
X`009&process_info->term_iosb,`009/* IO status block */
X`009&term_ast,`009`009`009/* Address of AST routine */
X`009process_info,`009`009`009/* AST argument */
X`009&term_buffer,`009`009`009/* Buffer address (P1) */
X`009sizeof term_buffer,`009`009/* Buffer length (P2) */
X`0090, 0, 0, 0);`009`009`009/* No more arguments (P3-P6) */
X`125
X#endif
X
X/* This AST routine is called when the subprocess terminates. */
Xstatic void
X#ifdef __STDC__
Xterm_ast(struct process *process_info)
X#else
Xterm_ast(process_info)
Xstruct process *process_info;
X#endif
X`123
X    SYS$CANCEL(process_info->status_channel);
X    process_info->pid = 0;
X`125
X
X#ifdef FANCY_OUTPUT
Xstatic void
X#ifdef __STDC__
Xstatus_ast(struct process *process_info)
X#else
Xstatus_ast(process_info)
Xstruct process *process_info;
X#endif
X`123
X    SYS$CANCEL(process_info->output_channel);
X`125
X#endif
X
Xstatic unsigned int
X#ifdef __STDC__
Xpost_status_read(struct process *process_info,
X`009`009 char *buffer_address, int buffer_length)
X#else
Xpost_status_read(process_info, buffer_address, buffer_length)
Xstruct process *process_info;
Xchar *buffer_address;
Xint buffer_length;
X#endif
X`123
X    unsigned int status;
X
X    /* Read in the output that the suprocess has written to the mailbox */
X    status = SYS$QIOW(
X`0090,`009`009`009`009/* No event flag */
X`009process_info->status_channel,`009/* IO channel */
X`009IO$_READVBLK,`009`009`009/* IO function - READ */
X`009&process_info->status_iosb,`009/* IO status block */
X#ifdef FANCY_OUTPUT
X`009&status_ast,`009`009`009/* AST address */
X#else
X`0090,
X#endif
X`009process_info,`009`009`009/* AST argument */
X`009buffer_address,`009`009`009/* Buffer address (P1) */
X`009buffer_length,`009`009`009/* Buffer length (P2) */
X`0090, 0, 0, 0);`009`009`009/* No more arguments (P3-P6) */
X    if (status & 1)
X`009status = process_info->status_iosb.status;
X    return status;
X`125
X
X#ifdef FANCY_OUTPUT
Xvoid
X#ifdef __STDC__
Xread_ast(struct process *process_info)
X#else
Xread_ast(process_info)
Xstruct process *process_info;
X#endif
X`123
X    /* Perhaps we should do something if iosb.status is other than SS$_ABORT
X       or SS$_CANCEL... */
X    if (!(process_info->output_iosb.status & 1))
X`009return;
X    if (*process_info->output)
X`009(*process_info->output)(process_info->output_buffer,
X`009`009`009`009process_info->output_iosb.count);
X    post_output_read(process_info);
X`125
X#endif
$ GOSUB UNPACK_FILE
$ FILE_IS = "SUBPROC.H"
$ CHECKSUM_IS = 1833038994
$ COPY SYS$INPUT VMS_SHARE_DUMMY.DUMMY
X/*
X * SUBPROC: VMS subprocess manipulation package.
X *
X * Version: 1.0 (1-FEB-1989)
X */
X
X#ifdef __STDC__
X# define PROTO(p) p
X#else
X# define PROTO(p)
X#endif
X
X/* Options... */
X
X/*#define USE_CREPRC`009`009/* If defined, use SYS$CREPRC to create
X`009`009`009`009   subprocesses, otherwise use LIB$SPAWN. */
X/*#define FANCY_OUTPUT`009`009/* If defined, add hooks to allow the output
X`009`009`009`009   from subprocesses to be passed back to a
X`009`009`009`009   routine supplied by the caller, otherwise
X`009`009`009`009   just send output to the SYS$OUTPUT. */
X
X#define DESCRIPTOR_N(name,string,len) struct dsc$descriptor_s name = \
X`009`123 (len), DSC$K_DTYPE_T, DSC$K_CLASS_S, (string) `125
X#define DESCRIPTOR(name,string) DESCRIPTOR_N(name,(string),strlen(string))
X
X#define DYNAMIC_DESCRIPTOR `123 0, DSC$K_DTYPE_T, DSC$K_CLASS_D, 0 `125
X
X/* Standard VAX/VMS item-list definitions.  These item-lists are used for
X   various system service calls.  There are two basic flavors, the three item
X   item-list and the two item item-list. */
X
Xstruct item_list_2
X`123
X    unsigned short buffer_length;`009/* Length of the buffer */
X    unsigned short item_code;`009`009/* Item code */
X    unsigned int *buffer_address;`009/* Address of the buffer */
X`125;
X
Xstruct item_list_3
X`123
X    unsigned short buffer_length;`009/* Length of the buffer */
X    unsigned short item_code;`009`009/* Item code */
X    unsigned int *buffer_address;`009/* Address of the buffer */
X    unsigned int *return_length_address;/* Returned information */
X`125;
X
X/* VAX/VMS I/O status block (IOSB) structure definition */
Xstruct io_status_block
X`123
X    unsigned short int status;`009`009/* Final status */
X    unsigned short int count;`009`009/* Byte count for many devices */
X    unsigned int devdepend;`009`009/* Device dependent part */
X`125;
X
Xstruct process
X`123
X    unsigned int pid;`009`009`009/* PID */
X
X    unsigned short input_channel;`009/* Input channel */
X    unsigned short input_unit;`009`009/* Input unit number */
X    struct dsc$descriptor input_name;`009/* Input mailbox name */
X    struct io_status_block input_iosb;`009/* Input IOSB */
X
X    unsigned short status_channel;`009/* Status channel */
X    unsigned short status_unit;`009`009/* Status unit number */
X    struct dsc$descriptor status_name;`009/* Status mailbox name */
X    struct io_status_block status_iosb;`009/* Status IOSB */
X
X#ifdef USE_CREPRC
X    unsigned short term_channel;`009/* Termination channel */
X    unsigned short term_unit;`009`009/* Termination unit number */
X    struct dsc$descriptor term_name;`009/* Termination mailbox name */
X    struct io_status_block term_iosb;`009/* Termination IOSB */
X#endif
X
X#ifdef FANCY_OUTPUT
X    unsigned short output_channel;`009/* Output channel */
X    unsigned short output_unit;`009`009/* Output unit number */
X    struct dsc$descriptor output_name;`009/* Output mailbox name */
X    struct io_status_block output_iosb;`009/* Output IOSB */
X
X    void (*output)(PROTO((char *, unsigned int)));
X    char output_buffer[255];/*???BOGUS???*/
X#endif
X`125;
X
X/* Initialize the DCL command processing.  Returns status code. */
Xunsigned int process_initialize(PROTO((struct process *process_info)));
X/* Shutdown the DCL command interface.  Returns status code. */
Xunsigned int process_shutdown(PROTO((struct process *process_info)));
X/* Execute a DCL command.  It will wait until the command is complete and
X   return the status of the executed command. */
Xunsigned int process_execute_command(PROTO((struct process *process_info,
X`009`009`009`009`009    struct dsc$descriptor *command,
X`009`009`009`009`009    unsigned int *command_status)));
X
X#ifdef FANCY_OUTPUT
X# define OUTPUT_NAME &process_info->output_name
X#else
X# define OUTPUT_NAME 0
X#endif
$ GOSUB UNPACK_FILE
$ FILE_IS = "TEST.C"
$ CHECKSUM_IS = 661935058
$ COPY SYS$INPUT VMS_SHARE_DUMMY.DUMMY
X#include <stdio.h>
X#include <descrip.h>
X#include "subproc.h"
X
X#ifdef __GNUC__
X# define VARIABLE 0
X#else
X# define VARIABLE
X#endif
X
Xstruct exit_handler_block `123
X    struct exit_handler_block *forward_link;
X    void (*handler)();
X    unsigned char argument_count;
X    unsigned char filler[3];
X    unsigned int *reason;
X    int arguments[VARIABLE];
X`125;
X
Xstatic void exit_handler(reason, proc)
Xunsigned int *reason;
Xstruct process *proc;
X`123
X    if (proc->pid)
X`009process_shutdown(proc);
X`125
X
Xmain()
X`123
X    static struct process proc = `123 0 `125;
X    struct exit_handler_block *handler;
V    static char *cmds[] = `123 "SHOW LOGICAL TT/FULL", "DIRECTORY", "SHOW TIM
XE" `125;
X    int i;
X    unsigned int reason;
X    unsigned int final_status;
X    unsigned int status;
X
X    handler =
X`009(struct exit_handler_block *)
X`009    malloc(sizeof (struct exit_handler_block)
X`009`009   + sizeof handler->arguments[0]);
X    handler->handler = exit_handler;
X    handler->argument_count = 1;
X    handler->reason = &reason;
X    handler->arguments[0] = (int) &proc;
X    status = SYS$DCLEXH(handler);
X    if (!(status & 1))
X    `123
X`009printf("*** DCLEXH failed with status = %08X\n", status);
X`009exit(1);
X    `125
X    status = process_initialize(&proc);
X    if (!(status & 1))
X    `123
X`009fprintf("*** Process initialization failed with status = %08X\n",
X`009`009status);
X`009exit(1);
X    `125
X    for (i = 0; i < (sizeof cmds / sizeof cmds[0]); i++)
X    `123
X`009DESCRIPTOR(command, cmds[i]);
X
X`009printf("$ %s\n", cmds[i]);
X`009status = process_execute_command(&proc, &command, &final_status);
X`009if (!(status & 1))
X`009    printf("*** DCL command failed with status = %08X\n", status);
X`009else
X`009    printf("The status of the command was: %08X\n", final_status);
X`009if (!proc.pid)
X`009`123
X`009    printf("*** The subprocess has exited.\n");
X`009    break;
X`009`125
X    `125
X    status = process_shutdown(&proc);
X    exit(status);
X`125
$ GOSUB UNPACK_FILE
$ EXIT
-+-+-+-+-+ End of part 2 +-+-+-+-+-

